OSDN Git Service

Initial
authorTakeyuki NAGAO <nagaotakeyuki@gmail.com>
Sun, 25 Mar 2012 14:28:06 +0000 (23:28 +0900)
committerTakeyuki NAGAO <nagaotakeyuki@gmail.com>
Sun, 25 Mar 2012 14:28:06 +0000 (23:28 +0900)
234 files changed:
src/jp/sourceforge/dvibrowser/dvi2epub/Dvi2EpubCmd.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/Test1.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AbstractCommand.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AnnotatedCommand.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AnnotatedCommandLine.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AnnotatedCommandLineParser.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/BooleanValueOption.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/Command.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandException.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandLine.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandLineParser.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandUtils.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CopyOfOptionAdapter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/DefaultOptionMapper.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/FormatAdapter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/IntValueOption.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/OptionAdapter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/OptionMapper.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/ParserState.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/cmd/StringValueOption.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/reflect/Dispatcher.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalker.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalkerAdapter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalkerException.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalkerHandler.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviByteRange.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviColor.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviConstants.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviException.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviFontName.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviFontSpec.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviFontTable.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviObject.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviPaperSize.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviPoint.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviRect.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviRectSplitter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviRegister.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviResolution.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviSerialized.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviSize.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviUniqueId.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/DviUnit.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/MetafontMode.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/BinaryDevice.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/CharacterCodeMapper.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/Device.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DevicePainter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DviCacheable.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DviContext.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DviContextSupport.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DviData.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DviDocument.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DviExecutor.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DviExecutorContext.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DviExecutorHandler.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DviFont.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DviInput.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/DviPage.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/FullMetrics.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/GammaCorrector.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/Geometer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/GeometerContext.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/Glyph.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/HasURL.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/ImageDevice.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/api/SimpleMetrics.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/cli/tools/ConvertToImage.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/cmd/DviBop.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/cmd/DviCommand.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/cmd/DviPostPost.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/cmd/DviPostamble.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/cmd/DviPreamble.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/AbstractDviResourceResolver.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/AsyncComputers.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/BakomaUnicodeCharacterCodeMapper.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/DefaultDviContext.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/DviToolkit.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/FileLocationResolver.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/KpseWhich.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/SimpleJisToUnicodeMapper.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/Type1DefaultCharacterCodeMapper.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/UnicodeCharacterCodeMapper.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/cmsy-enc.csv [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/default-context.properties [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/ctx/ot1-enc.csv [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/doc/DefaultDviPage.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/doc/DirectFileDviDocument.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/doc/StreamDviDocument.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/doc/URLDviDocument.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/event/TDefaultEventModel.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/event/TEvent.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/event/TEventListener.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/event/TEventModel.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/event/TEventMulticaster.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/event/TEventProcessor.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/event/TEventQueue.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/AWTDynamicPkFont.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/AWTDynamicPkFontResolver.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/AbstractDviFontResolver.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/AbstractDynamicPkFont.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/AbstractMetricsResolver.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/DviFontResolver.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/FullMetricsResolver.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/LogicalFont.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/LogicalGlyph.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/PackedGlyphRasterizer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/PackedSequence.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/PkConstants.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/PkFont.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/PkFontResolver.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/PkGlyph.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/RunLengthEncodedGlyph.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/RunLengthEncodedLine.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/SequencePacker.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/TexFontMetrics.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/TrueTypeFontResolver.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/Type1FontResolver.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/VirtualFont.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/font/VirtualFontResolver.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/gs/GhostscriptBBOXParser.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/gs/GhostscriptCommandBuilder.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/gs/GhostscriptUtils.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/gui/swing/DefaultDviLayoutManager.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/gui/swing/DragToScroll.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/gui/swing/DviLayoutManager.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TDviDocument.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TDviPage.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TScrollPane.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TexLogViewer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/gui/swing/ViewSpec.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/pnm/AbstractPnmAsciiFilter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/pnm/AbstractPnmSplitter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PbmSplitter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PgmSplitter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmBitAsciiFilter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmByteAsciiFilter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmHeader.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmSplitter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PpmSplitter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/AbstractSplitPiece.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/DefaultSplitImageWriter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/DviImage.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/FileImagePiece.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/ImageFileConfig.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/ImageSplitter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitImage.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitImageUtils.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitImageWriter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitPiece.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/URLImagePiece.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/ZipImagePiece.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/ZipSplitImageReader.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/image/split/ZipSplitImageWriter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/io/ByteArrayDviData.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/io/DviByteBufferInput.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/io/DviInputStreamReader.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/io/DviRandomAccessFileInput.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/plat/cygwin/CygwinUtils.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/AbstractDevice.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/BasicExecutor.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/BasicGeometer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/BinaryImage.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/BoundingBoxComputer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/ByteRGBImage.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/ByteRangeComputer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/DefaultDevicePainter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/DefaultGammaCorrector.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/DumpBinaryDevice.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/DumpHandler.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/DviBoundingBoxPreparator.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/DviExecutorFilter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/DviPagePreparator.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/EmptyBinaryDevice.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/EmptyDevicePainter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/EmptyDviExecutorHandler.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/GammaCorrectorCache.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/IntRGBImage.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/RunLengthSampler.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/StopHandler.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/render/VirtualFontGeometer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/special/AbstractDviSpecialExecutor.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/special/Anchor.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/special/AnchorSet.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/special/ByteRangeSet.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/special/EPS2ImagePreparator.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/special/EPS2SplitImagePreparator.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/special/EmbeddedPostScript.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/special/EmbeddedPostScriptPreparator.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/special/HtmlSpecialParser.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/special/PostScriptSpecialParser.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/special/SourceSpecialParser.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/Benchmark.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/BufferFilter.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/Canonicalizer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/CommandShell.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/CommandShellHandler.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/DaemonThreadFactory.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/DefaultCommandShellHandler.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/DumpCommandShellHandler.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/DviCache.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/DviDesktop.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/DviInfoDumper.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/DviUtils.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/LineBuffer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/SimpleCanonicalizer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/TeXMessageParser.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/ZipBuilder.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/AbstractComputer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/BasicComputer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/CacheEntry.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/Cacheable.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/CachedComputer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/Computation.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/Computer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/ComputerProgressMonitor.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/ThreadedComputer.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvCellCodec.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvData.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvException.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvLineParser.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvParser.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/csv/StringCsvCellCodec.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/csv/StringCsvData.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/progress/AbstractProgressModel.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/progress/ManagedProgressItem.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressBlock.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressEvent.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressItem.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressListener.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressLogger.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressMessage.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressRecorder.java [new file with mode: 0644]
src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressReporter.java [new file with mode: 0644]

diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/Dvi2EpubCmd.java b/src/jp/sourceforge/dvibrowser/dvi2epub/Dvi2EpubCmd.java
new file mode 100644 (file)
index 0000000..ac6b212
--- /dev/null
@@ -0,0 +1,47 @@
+package jp.sourceforge.dvibrowser.dvi2epub;
+
+import java.util.Arrays;
+
+import jp.sourceforge.dvibrowser.dvi2epub.cmd.AnnotatedCommand;
+import jp.sourceforge.dvibrowser.dvi2epub.cmd.BooleanValueOption;
+import jp.sourceforge.dvibrowser.dvi2epub.cmd.Command;
+import jp.sourceforge.dvibrowser.dvi2epub.cmd.CommandException;
+import jp.sourceforge.dvibrowser.dvi2epub.cmd.CommandUtils;
+import jp.sourceforge.dvibrowser.dvi2epub.cmd.IntValueOption;
+import jp.sourceforge.dvibrowser.dvi2epub.cmd.StringValueOption;
+
+public class Dvi2EpubCmd extends AnnotatedCommand {
+
+       @Override
+       protected int processCommandLine() throws CommandException {
+               System.out.println("Arguments: " + Arrays.toString(getArgs()));
+               return Command.EXIT_SUCCESS;
+       }
+       
+       @BooleanValueOption(description = "test", longName = "debug", shortName = "d")
+       public void wantDebug(boolean want)
+       {
+               System.out.println("test");
+       }
+       
+       @StringValueOption(description = "test2", longName = "debug2", shortName = "x", value = Command.NULL)
+       public void setParam1(String s)
+       {
+               System.out.println("test2: " + s);
+       }
+       
+       @IntValueOption(description = "test3", longName = "debug3", shortName = "i", value = 3)
+       public void setParam2(int s)
+       {
+               System.out.println("test3: " + s);
+       }
+       
+       @Override
+       public boolean wantExit() {
+               return false;
+       }
+       
+       public static void main(String[] args) throws CommandException {
+               CommandUtils.executeCommand(Dvi2EpubCmd.class, args);
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/Test1.java b/src/jp/sourceforge/dvibrowser/dvi2epub/Test1.java
new file mode 100644 (file)
index 0000000..08d00d6
--- /dev/null
@@ -0,0 +1,44 @@
+package jp.sourceforge.dvibrowser.dvi2epub;
+
+import jp.sourceforge.dvibrowser.dvi2epub.reflect.Dispatcher;
+import jp.sourceforge.dvibrowser.dvi2epub.reflect.MemberWalkerException;
+
+public class Test1 {
+       public void test(String data)
+       {
+               System.out.println("String data: " + data);
+       }
+       
+       public void test(Object o)
+       {
+               System.out.println("Object data: " + o);
+       }
+       
+       public static void main(String[] args) throws MemberWalkerException {
+               Test1 test1 = new Test1();
+               Object o = "hoge";
+               String s = "foo";
+               System.out.println("Class: " + o.getClass());
+               test1.test(o);
+               test1.test(s);
+               
+               {
+                       Dispatcher dispatcher = new Dispatcher(test1, "test", o);
+                       if (dispatcher.dispatch()) {
+                               System.out.println("Output = " + dispatcher.getResult());
+                       } else {
+                               System.out.println("Invocation failure.");
+                       }
+               }
+               
+               {
+                       Dispatcher dispatcher = new Dispatcher(test1, "test", 123);
+                       if (dispatcher.dispatch()) {
+                               System.out.println("Output = " + dispatcher.getResult());
+                       } else {
+                               System.out.println("Invocation failure.");
+                       }
+               }
+
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AbstractCommand.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AbstractCommand.java
new file mode 100644 (file)
index 0000000..bfc5806
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Take-Yuki NAGAO
+ * 
+ * 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 jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+import java.io.PrintWriter;
+
+public abstract class AbstractCommand implements Command {
+       private PrintWriter writer = new PrintWriter(System.out, true);
+
+       public AbstractCommand() {
+       }
+
+       public abstract int execute(String[] args) throws CommandException;
+
+       public void showUsage() throws CommandException {
+               showUsage(getWriter());
+       }
+
+       public void showUsage(PrintWriter out) throws CommandException {
+               out.println(getApplicationName() + " " + getCommandLineSyntax());
+               out.flush();
+       }
+
+       public String getCommandLineSyntax() throws CommandException {
+               return "[options]";
+       }
+
+       public String getApplicationName() throws CommandException {
+               return getClass().getName();
+       }
+
+       public void setWriter(PrintWriter writer) {
+               this.writer = writer;
+       }
+
+       public PrintWriter getWriter() {
+               return writer;
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AnnotatedCommand.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AnnotatedCommand.java
new file mode 100644 (file)
index 0000000..df78d0f
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012 Take-Yuki NAGAO
+ * 
+ * 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 jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+import java.io.PrintWriter;
+
+public abstract class AnnotatedCommand extends AbstractCommand {
+       private CommandLineParser parser;
+       private boolean wantHelp;
+       private String[] args;
+
+       public AnnotatedCommand() {
+               parser = new AnnotatedCommandLineParser(this);
+       }
+
+       @BooleanValueOption(shortName = "h", longName = "help", description = "Show help")
+       public void wantHelp(boolean want) {
+               this.wantHelp = want;
+       }
+
+       public boolean wantHelp() {
+               return wantHelp;
+       }
+
+       @Override
+       public int execute(String[] args) throws CommandException {
+               try {
+                       parser.parse(args);
+                       int ret = doProcessCommandLine();
+                       return ret;
+               } finally {
+                       PrintWriter out = getWriter();
+                       if (out != null) {
+                               out.flush();
+                       }
+               }
+       }
+
+       protected int doProcessCommandLine()
+                       throws CommandException {
+               if (wantHelp()) {
+                       showUsage();
+                       return Command.EXIT_SUCCESS;
+               } else {
+                       return processCommandLine();
+               }
+       }
+
+       protected abstract int processCommandLine() throws CommandException;
+
+       @Override
+       public void showUsage(PrintWriter pw) throws CommandException {
+               parser.printHelp(pw);
+               pw.flush();
+       }
+
+       @Override
+       public String getApplicationName() throws CommandException {
+               return getClass().getName();
+       }
+
+       public String[] getArgs() {
+               return args;
+       }
+
+       public void setArgs(String[] args) {
+               this.args = args;
+       }
+       
+       protected OptionMapper createOptionMapper(ParserState state) {
+               return new DefaultOptionMapper(state);
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AnnotatedCommandLine.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AnnotatedCommandLine.java
new file mode 100644 (file)
index 0000000..2060cba
--- /dev/null
@@ -0,0 +1,24 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+public class AnnotatedCommandLine implements CommandLine {
+       private final String[] args;
+       private final AnnotatedCommand command;
+
+       public AnnotatedCommandLine(AnnotatedCommand command, String[] args) {
+               this.command = command;
+               this.args = args;
+       }
+
+       @Override
+       public boolean hasOption(String name) {
+               return false;
+       }
+
+       public String[] getArgs() {
+               return args;
+       }
+
+       public AnnotatedCommand getCommand() {
+               return command;
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AnnotatedCommandLineParser.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/AnnotatedCommandLineParser.java
new file mode 100644 (file)
index 0000000..ef285b9
--- /dev/null
@@ -0,0 +1,93 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+import java.io.PrintWriter;
+
+import jp.sourceforge.dvibrowser.dvi2epub.reflect.MemberWalker;
+import jp.sourceforge.dvibrowser.dvi2epub.reflect.MemberWalkerException;
+
+public class AnnotatedCommandLineParser implements CommandLineParser {
+       private final AnnotatedCommand command;
+
+       public AnnotatedCommandLineParser(AnnotatedCommand cmd) {
+               this.command = cmd;
+       }
+
+       @Override
+       public CommandLine parse(String[] args) throws CommandException {
+               CommandLine commandLine = new AnnotatedCommandLine(command, args);
+               ParserState state = new ParserState(args);
+               while (!state.wantStop()) {
+                       String arg1 = state.shift();
+//                     System.out.println("arg1=" + arg1);
+                       if (arg1 == null) {
+                               state.wantStop(true);
+                       } else if (arg1.startsWith("--")) {
+                               // Long parameter
+                               parseLongOption(state, arg1);
+                       } else if ("-".equals(arg1)) {
+                               state.wantStop(true);
+                       } else if (arg1.startsWith("-")) {
+                               // Short parameter
+                               parseShortOption(state, arg1);
+                       } else {
+                               // No parameter
+                               state.unshift(arg1);
+                               state.wantStop(true);
+                       }
+               }
+               
+               if (state.hasError()) {
+                       throw new CommandException(state.getThrowable());
+               }
+               
+               command.setArgs(state.getList().toArray(new String [0]));
+               
+               return commandLine;
+       }
+
+       private void parseShortOption(final ParserState state, final String arg1) throws CommandException {
+               char c = arg1.charAt(1);
+               String arguments = null;
+               if (arg1.length() > 2) {
+                       arguments = arg1.substring(2);
+               }
+               doParseOption(state, "" + c, arguments);
+       }
+
+       private void parseLongOption(final ParserState state, final String arg1) throws CommandException {
+               String name = arg1.substring(2);
+               String arguments = null;
+               int pos = name.indexOf('=');
+               if (-1 != pos) {
+                       arguments = name.substring(pos + 1);
+                       name = name.substring(0, pos);
+               }
+               
+               doParseOption(state, name, arguments);
+       }
+       
+       private void doParseOption(final ParserState state, final String name, final String parameter) throws CommandException
+       {
+               state.setOptionName(name);
+               state.setOptionParameter(parameter);
+               OptionMapper mapper = command.createOptionMapper(state);
+               
+               MemberWalker walker = new MemberWalker(new OptionAdapter(state, mapper));
+               try {
+                       walker.walk(command);
+               } catch (MemberWalkerException e) {
+                       throw new CommandException(e);
+               }
+       }
+
+       public AnnotatedCommand getCommand() {
+               return command;
+       }
+
+       @Override
+       public void printHelp(PrintWriter pw) throws CommandException {
+               // pw may not be null.
+               pw.println("Usage: " + command.getApplicationName() + " " + command.getCommandLineSyntax());
+               pw.flush();
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/BooleanValueOption.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/BooleanValueOption.java
new file mode 100644 (file)
index 0000000..b5d195c
--- /dev/null
@@ -0,0 +1,12 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface BooleanValueOption {
+       String shortName();
+       String longName();
+       String description();
+       boolean value() default true;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/Command.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/Command.java
new file mode 100644 (file)
index 0000000..3bf402a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2012 Take-Yuki NAGAO
+ * 
+ * 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 jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+public interface Command {
+       public static final int EXIT_SUCCESS = 0;
+       public static final int EXIT_ERROR = 1;
+       public static final String NULL = "-NULL-";
+       
+       int execute(String[] args) throws CommandException;
+       
+       boolean wantExit();
+
+       void showUsage() throws CommandException;
+
+       String getApplicationName() throws CommandException;
+
+       String getCommandLineSyntax() throws CommandException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandException.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandException.java
new file mode 100644 (file)
index 0000000..e8b1036
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012 Take-Yuki NAGAO
+ * 
+ * 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 jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+public class CommandException extends Exception {
+       private static final long serialVersionUID = 9105846149523606971L;
+
+       public CommandException() {
+       }
+
+       public CommandException(String arg0) {
+               super(arg0);
+       }
+
+       public CommandException(Throwable arg0) {
+               super(arg0);
+       }
+
+       public CommandException(String arg0, Throwable arg1) {
+               super(arg0, arg1);
+       }
+
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandLine.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandLine.java
new file mode 100644 (file)
index 0000000..049146b
--- /dev/null
@@ -0,0 +1,5 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+public interface CommandLine {
+       boolean hasOption(String name);
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandLineParser.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandLineParser.java
new file mode 100644 (file)
index 0000000..ca01862
--- /dev/null
@@ -0,0 +1,8 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+import java.io.PrintWriter;
+
+public interface CommandLineParser {
+       CommandLine parse(String [] args) throws CommandException;
+       void printHelp(PrintWriter pw) throws CommandException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandUtils.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CommandUtils.java
new file mode 100644 (file)
index 0000000..2cfefc5
--- /dev/null
@@ -0,0 +1,46 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+public class CommandUtils {
+       private CommandUtils() {}
+
+       public static int executeCommand(Class<?> cls, String[] args)
+                       throws CommandException {
+               try {
+                       if (Command.class.isAssignableFrom(cls)) {
+                               Command cmd = (Command) cls.newInstance();
+                               int ret = cmd.execute(args);
+                               if (cmd.wantExit()) {
+                                       System.exit(ret);
+                               }
+                               return ret;
+                       } else {
+                               run(cls.getName(), args);
+                               return Command.EXIT_SUCCESS;
+                       }
+               } catch (Exception e) {
+                       throw new CommandException(e);
+               }
+       }
+       
+       public static void run(String classname, String[] args) throws Exception {
+               Class<?> cls = Class.forName(classname);
+               cls.getMethod("main", new Class[] { String[].class })
+                 .invoke(null, new Object[] { args });
+       }
+       
+       public static boolean parseBoolean(String arg, boolean value) {
+               if (arg == null) return value;
+               arg = arg.toLowerCase().trim();
+               if (arg.equals("true") || arg.equals("yes")) {
+                       return true;
+               }
+               return false;
+       }
+       
+       public static <T> T unescapeNull(T value)
+       {
+               if (value instanceof String && Command.NULL.equals(value))
+                       return null;
+               return value;
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CopyOfOptionAdapter.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/CopyOfOptionAdapter.java
new file mode 100644 (file)
index 0000000..6fecd2f
--- /dev/null
@@ -0,0 +1,110 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import jp.sourceforge.dvibrowser.dvi2epub.reflect.MemberWalkerAdapter;
+import jp.sourceforge.dvibrowser.dvi2epub.reflect.MemberWalkerException;
+
+final class CopyOfOptionAdapter extends MemberWalkerAdapter {
+       private final String name;
+       private final String arguments;
+       private boolean done;
+       private final ParserState state;
+       private List<Class<?>> classes;
+       private String targetMethod;
+
+       CopyOfOptionAdapter(ParserState state, String name, String arguments) {
+               this.name = name;
+               this.arguments = arguments;
+               this.state = state;
+               targetMethod = "buildArgs";
+               classes = new ArrayList<Class<?>>();
+               for (Method mm : this.getClass().getDeclaredMethods()) {
+                       if (mm.getName().equals(targetMethod)) {
+                               Class<?>[] clss = mm.getParameterTypes();
+                               if (clss.length > 0) {
+                                       Class<?> cc = clss[0];
+                                       if (cc.isAnnotation()) {
+                                               classes.add(cc);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       public boolean wantMember(Object o, Member member) {
+               return !done;
+       }
+       
+       public void setDone(boolean value) {
+               done = value;
+       }
+       
+       public boolean isDone() {
+               return done;
+       }
+
+       public void processMethod(Object o, Method method)
+                       throws MemberWalkerException {
+               Annotation[] annotations = method.getDeclaredAnnotations();
+               try {
+                       for (Annotation a : annotations) {
+                               for (Class<?> cls : classes) {
+                                       if (a.annotationType().equals(cls)) {
+                                               // System.out.println("cls=" + cls);
+                                               Method methodS = cls.getDeclaredMethod("shortName",
+                                                               new Class<?>[] {});
+                                               Method methodL = cls.getDeclaredMethod("longName",
+                                                               new Class<?>[] {});
+                                               String shortName = (String) methodS.invoke(a);
+                                               String longName = (String) methodL.invoke(a);
+                                               if (nameHits(name, shortName, longName)) {
+                                                       // System.out.println("shortName=" + shortName +
+                                                       // " longName=" + longName);
+                                                       Method m = this.getClass().getDeclaredMethod(
+                                                                       "buildArgs", new Class<?>[] { cls });
+                                                       Object[] args = (Object[]) m.invoke(this,
+                                                                       new Object[] { a });
+                                                       method.invoke(o, args);
+                                                       setDone(true);
+                                               }
+                                       }
+                               }
+                       }
+               } catch (Exception ex) {
+                       ex.printStackTrace();
+                       state.stopWithError(ex);
+                       return;
+               }
+       }
+
+       private boolean nameHits(String name, String shortName, String longName) {
+               if (shortName != null && shortName.equals(name)) {
+                       return true;
+               } else if (longName != null && longName.equals(name)) {
+                       return true;
+               }
+               return false;
+       }
+
+       protected Object[] buildArgs(IntValueOption p) {
+               String a = (arguments == null) ? state.shift() : arguments;
+               int value = (a == null) ? p.value() : Integer.parseInt(a);
+               return new Object[] { value };
+       }
+
+       protected Object[] buildArgs(StringValueOption p) {
+               String value = CommandUtils.unescapeNull((CommandUtils
+                               .unescapeNull(arguments) == null) ? p.value() : arguments);
+               return new Object[] { value };
+       }
+
+       protected Object[] buildArgs(BooleanValueOption p) {
+               boolean value = CommandUtils.parseBoolean(arguments, p.value());
+               return new Object[] { value };
+       }
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/DefaultOptionMapper.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/DefaultOptionMapper.java
new file mode 100644 (file)
index 0000000..19fda9c
--- /dev/null
@@ -0,0 +1,35 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+public class DefaultOptionMapper
+implements OptionMapper
+{
+       private final ParserState state;
+
+       public DefaultOptionMapper(ParserState state) {
+               this.state = state;
+       }
+       
+       public Object[] map(IntValueOption p) {
+               String param = getState().getOptionParameter();
+               String a = (param == null) ? getState().shift() : param;
+               int value = (a == null) ? p.value() : Integer.parseInt(a);
+               return new Object[] { value };
+       }
+
+       public Object[] map(StringValueOption p) {
+               String param = getState().getOptionParameter();
+               String value = CommandUtils.unescapeNull((CommandUtils
+                               .unescapeNull(param) == null) ? p.value() : param);
+               return new Object[] { value };
+       }
+
+       public Object[] map(BooleanValueOption p) {
+               String param = getState().getOptionParameter();
+               boolean value = CommandUtils.parseBoolean(param, p.value());
+               return new Object[] { value };
+       }
+       
+       public ParserState getState() {
+               return state;
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/FormatAdapter.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/FormatAdapter.java
new file mode 100644 (file)
index 0000000..ea42794
--- /dev/null
@@ -0,0 +1,110 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import jp.sourceforge.dvibrowser.dvi2epub.reflect.MemberWalkerAdapter;
+import jp.sourceforge.dvibrowser.dvi2epub.reflect.MemberWalkerException;
+
+final class FormatAdapter extends MemberWalkerAdapter {
+       private final String name;
+       private final String arguments;
+       private boolean done;
+       private final ParserState state;
+       private List<Class<?>> classes;
+       private String targetMethod;
+
+       FormatAdapter(ParserState state, String name, String arguments) {
+               this.name = name;
+               this.arguments = arguments;
+               this.state = state;
+               targetMethod = "buildArgs";
+               classes = new ArrayList<Class<?>>();
+               for (Method mm : this.getClass().getDeclaredMethods()) {
+                       if (mm.getName().equals(targetMethod)) {
+                               Class<?>[] clss = mm.getParameterTypes();
+                               if (clss.length > 0) {
+                                       Class<?> cc = clss[0];
+                                       if (cc.isAnnotation()) {
+                                               classes.add(cc);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       public boolean wantMember(Object o, Member member) {
+               return !done;
+       }
+       
+       public void setDone(boolean value) {
+               done = value;
+       }
+       
+       public boolean isDone() {
+               return done;
+       }
+
+       public void processMethod(Object o, Method method)
+                       throws MemberWalkerException {
+               Annotation[] annotations = method.getDeclaredAnnotations();
+               try {
+                       for (Annotation a : annotations) {
+                               for (Class<?> cls : classes) {
+                                       if (a.annotationType().equals(cls)) {
+                                               // System.out.println("cls=" + cls);
+                                               Method methodS = cls.getDeclaredMethod("shortName",
+                                                               new Class<?>[] {});
+                                               Method methodL = cls.getDeclaredMethod("longName",
+                                                               new Class<?>[] {});
+                                               String shortName = (String) methodS.invoke(a);
+                                               String longName = (String) methodL.invoke(a);
+                                               if (nameHits(name, shortName, longName)) {
+                                                       // System.out.println("shortName=" + shortName +
+                                                       // " longName=" + longName);
+                                                       Method m = this.getClass().getDeclaredMethod(
+                                                                       "buildArgs", new Class<?>[] { cls });
+                                                       Object[] args = (Object[]) m.invoke(this,
+                                                                       new Object[] { a });
+                                                       method.invoke(o, args);
+                                                       setDone(true);
+                                               }
+                                       }
+                               }
+                       }
+               } catch (Exception ex) {
+                       ex.printStackTrace();
+                       state.stopWithError(ex);
+                       return;
+               }
+       }
+
+       private boolean nameHits(String name, String shortName, String longName) {
+               if (shortName != null && shortName.equals(name)) {
+                       return true;
+               } else if (longName != null && longName.equals(name)) {
+                       return true;
+               }
+               return false;
+       }
+
+       protected Object[] buildArgs(IntValueOption p) {
+               String a = (arguments == null) ? state.shift() : arguments;
+               int value = (a == null) ? p.value() : Integer.parseInt(a);
+               return new Object[] { value };
+       }
+
+       protected Object[] buildArgs(StringValueOption p) {
+               String value = CommandUtils.unescapeNull((CommandUtils
+                               .unescapeNull(arguments) == null) ? p.value() : arguments);
+               return new Object[] { value };
+       }
+
+       protected Object[] buildArgs(BooleanValueOption p) {
+               boolean value = CommandUtils.parseBoolean(arguments, p.value());
+               return new Object[] { value };
+       }
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/IntValueOption.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/IntValueOption.java
new file mode 100644 (file)
index 0000000..3e92101
--- /dev/null
@@ -0,0 +1,12 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface IntValueOption {
+       String shortName();
+       String longName();
+       String description();
+       int value() default 0;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/OptionAdapter.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/OptionAdapter.java
new file mode 100644 (file)
index 0000000..97e383e
--- /dev/null
@@ -0,0 +1,71 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+import jp.sourceforge.dvibrowser.dvi2epub.reflect.Dispatcher;
+import jp.sourceforge.dvibrowser.dvi2epub.reflect.MemberWalkerAdapter;
+import jp.sourceforge.dvibrowser.dvi2epub.reflect.MemberWalkerException;
+
+final class OptionAdapter extends MemberWalkerAdapter {
+       private final ParserState state;
+       private final OptionMapper mapper;
+
+       OptionAdapter(ParserState state, OptionMapper mapper) {
+               this.state = state;
+               this.mapper = mapper;
+       }
+
+       public void processMethod(Object o, Method method)
+                       throws MemberWalkerException {
+               Annotation[] annotations = method.getDeclaredAnnotations();
+               try {
+                       for (Annotation a : annotations) {
+                               String shortName = null, longName = null;
+                               
+                               try {
+                                       Method m = a.getClass().getDeclaredMethod("shortName",
+                                                               new Class<?>[] {});
+                                       shortName = (String) m.invoke(a);
+                               } catch (NoSuchMethodException ex) {
+                                       // Ignored.
+                               }
+                               try {
+                                       Method m = a.getClass().getDeclaredMethod("longName",
+                                                       new Class<?>[] {});
+                                       longName = (String) m.invoke(a);
+                               } catch (NoSuchMethodException ex) {
+                                       // Ignored.
+                               }
+
+                               if (nameHits(state.getOptionName(), shortName, longName)) {
+                                       Dispatcher<Object[]> d = new Dispatcher<Object []>(getMapper(), "map", a);
+                                       if (d.dispatch()) {
+                                               Object [] args = d.getResult();
+//                                             System.out.println("Dispatch result: " + Arrays.toString(args));
+                                               method.invoke(o, args);
+                                               setDone(true);
+                                       }
+                               }
+                       }
+               } catch (Exception ex) {
+//                     ex.printStackTrace();
+                       state.stopWithError(ex);
+                       return;
+               }
+       }
+       
+       public OptionMapper getMapper()
+       {
+               return mapper;
+       }
+
+       private boolean nameHits(String name, String shortName, String longName) {
+               if (shortName != null && shortName.equals(name)) {
+                       return true;
+               } else if (longName != null && longName.equals(name)) {
+                       return true;
+               }
+               return false;
+       }
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/OptionMapper.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/OptionMapper.java
new file mode 100644 (file)
index 0000000..bafa228
--- /dev/null
@@ -0,0 +1,5 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+public interface OptionMapper {
+       ParserState getState();
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/ParserState.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/ParserState.java
new file mode 100644 (file)
index 0000000..cb77993
--- /dev/null
@@ -0,0 +1,80 @@
+package jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ParserState {
+       private final String[] args;
+       private List<String> list = new ArrayList<String>();
+       private Throwable throwable;
+       private boolean stop = false;
+       private String optionName;
+       private String optionParameter;
+
+       public ParserState(String [] args) {
+               this.args = args;
+               this.list.addAll(Arrays.asList(args));
+       }
+
+       public String[] getOriginalArgs() {
+               return args;
+       }
+
+       public List<String> getList() {
+               return list;
+       }
+
+       public String shift() {
+               String ret = null;
+               if (list.size() > 0) {
+                       ret = list.remove(0);
+               }
+               return ret;
+       }
+       
+       public void unshift(String value) {
+               list.add(0, value);
+       }
+
+       public boolean wantStop() {
+               return stop;
+       }
+
+       public void wantStop(boolean stop) {
+               this.stop = stop;
+       }
+       
+       public void setError(Throwable ex) {
+               this.throwable = ex;
+       }
+       
+       public void stopWithError(Throwable ex) {
+               setError(ex);
+               wantStop(true);
+       }
+
+       public Throwable getThrowable() {
+               return this.throwable;
+       }
+       
+       public boolean hasError() {
+               return getThrowable() != null;
+       }
+
+       public String getOptionParameter() {
+               return optionParameter;
+       }
+
+       public void setOptionParameter(String optionParameter) {
+               this.optionParameter = optionParameter;
+       }
+
+       public String getOptionName() {
+               return optionName;
+       }
+
+       public void setOptionName(String optionName) {
+               this.optionName = optionName;
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/StringValueOption.java b/src/jp/sourceforge/dvibrowser/dvi2epub/cmd/StringValueOption.java
new file mode 100644 (file)
index 0000000..98eafee
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 Take-Yuki NAGAO
+ * 
+ * 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 jp.sourceforge.dvibrowser.dvi2epub.cmd;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface StringValueOption {
+       String shortName();
+       String longName();
+       String description();
+       String value();
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/reflect/Dispatcher.java b/src/jp/sourceforge/dvibrowser/dvi2epub/reflect/Dispatcher.java
new file mode 100644 (file)
index 0000000..c2ccb85
--- /dev/null
@@ -0,0 +1,76 @@
+package jp.sourceforge.dvibrowser.dvi2epub.reflect;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+
+public class Dispatcher<T>
+extends MemberWalkerAdapter
+{
+       private final String methodName;
+       private final Object[] args;
+       private final Object mapper;
+       private T result;
+       private boolean success;
+
+       public Dispatcher(Object mapper, String methodName, Object ... args) throws IllegalArgumentException
+       {
+               this.mapper = mapper;
+               this.methodName = methodName;
+               this.args = args;
+               if (args.length < 1)
+                       throw new IllegalArgumentException("Too few arguments to invoke().");
+       }
+       
+       public void processMethod(Object target, Method method) throws MemberWalkerException {
+               try {
+                       if (method.getName().equals(getMethodName())) {
+                               Class<?>[] clss = method.getParameterTypes();
+                               if (clss.length > 0) {
+                                       Class<?> cc = clss[0];
+                                       if (cc.isAssignableFrom(args[0].getClass())) {
+                                               Object ret = method.invoke(target, getArgs());
+                                               result = (T) ret;
+                                               setDone(true);
+                                               success = true;
+                                       }
+                               }
+                       }
+               } catch (SecurityException e) {
+                       throw new MemberWalkerException(e);
+               } catch (IllegalArgumentException e) {
+                       throw new MemberWalkerException(e);
+               } catch (IllegalAccessException e) {
+                       throw new MemberWalkerException(e);
+               } catch (InvocationTargetException e) {
+                       throw new MemberWalkerException(e);
+               }
+       }
+       
+       public boolean dispatch() throws MemberWalkerException
+       {
+               MemberWalker walker = new MemberWalker(this);
+               walker.walk(mapper);
+               return success;
+       }
+
+       public String getMethodName() {
+               return methodName;
+       }
+
+       public Object[] getArgs() {
+               return args;
+       }
+
+       public Object getMapper() {
+               return mapper;
+       }
+
+       public T getResult() {
+               return result;
+       }
+
+       protected void setResult(T result) {
+               this.result = result;
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalker.java b/src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalker.java
new file mode 100644 (file)
index 0000000..c2fa6b2
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+The MIT License
+
+Copyright (c) 2010 Takeyuki Nagao
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+ */
+
+package jp.sourceforge.dvibrowser.dvi2epub.reflect;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+/**
+ * <p>
+ * \8ew\92è\83I\83u\83W\83F\83N\83g\82Ì\82·\82×\82Ä\82Ì\83\81\83\93\83o\81i\83t\83B\81[\83\8b\83h\8by\82Ñ\83\81\83\\83b\83h\81j\82ð\8f\84\89ñ\82µ\82Ä\81\8cÂ\81X\82Ì\83\81\83\93\83o\82É\91Î\82µ\82Ä\93Á\92è\82Ì\8f\88\97\9d\82ð\8ds\82¤\82½\82ß\82Ì\83N\83\89\83X\81\8bï\91Ì\93I\82È\8f\88\97\9d\82Ì\93à\97e\82Í
+ * {@link MemberWalkerHandler} \83C\83\93\83^\81[\83t\83F\83C\83X\82Ì\8eÀ\91\95\83N\83\89\83X\82É\8bL\8fq\82µ\82Ä\81C\83R\83\93\83X\83g\83\89\83N\83^\88ø\90\94\82Å\82»\82Ì\83C\83\93\83X\83^\83\93\83X\82ð\8ew\92è\82·\82é\81D
+ * </p>
+ * <p>
+ * \97\98\97p\97á\82ð\8b\93\82°\82é\81D <code><pre>
+    MyBean bean = new MyBean(); // \83\86\81[\83U\92è\8b`\83N\83\89\83X
+    bean.setField1("hogehoge");
+    bean.setField2(314);
+    
+    MemberWalker walker = new MemberWalker(new MemberWalkerAdapter() {
+      public void processField(Object o, Field field)
+      {
+        System.out.println("\83t\83B\81[\83\8b\83h: " + field.getName());
+      }
+    });
+    walker.walk(bean);
+ * </pre></code>
+ * </p>
+ * 
+ * @author nagaotakeyuki@gmail.com
+ */
+
+public class MemberWalker {
+       private final MemberWalkerHandler handler;
+
+       /**
+        * <p>
+        * \8ew\92è\82³\82ê\82½\83n\83\93\83h\83\89\82ð\8e\9d\82Á\82½\83C\83\93\83X\83^\83\93\83X\82ð\90\90¬\82·\82é\81D
+        * </p>
+        * 
+        * @param handler
+        *            \83\81\83\93\83o\82ð\8c©\82Â\82¯\82½\82Æ\82«\82É\8cÄ\82Ñ\8fo\82³\82ê\82é\83n\83\93\83h\83\89
+        */
+       public MemberWalker(MemberWalkerHandler handler) {
+               this.handler = handler;
+               if (handler == null) {
+                       throw new NullPointerException("Handler can't be null.");
+               }
+       }
+
+       protected boolean wantMember(Object t, Member member)
+                       throws MemberWalkerException {
+               return handler.wantMember(t, member);
+       }
+
+       /**
+        * \8ew\92è\82³\82ê\82½\83I\83u\83W\83F\83N\83g\82Ì\82·\82×\82Ä\82Ì\83\81\83\93\83o\81i\83t\83B\81[\83\8b\83h\82Æ\83\81\83\\83b\83h\81j\82É\91Î\82µ\82Ä\83n\83\93\83h\83\89\82ð\8cÄ\82Ñ\8fo\82·\81D <code>public</code>
+        * \82Å\82È\82¢\83\81\83\93\83o\82à\8f\88\97\9d\82Ì\91Î\8fÛ\82Æ\82È\82é\81D
+        * 
+        * @param t
+        *            \91Î\8fÛ\82Æ\82È\82é\83I\83u\83W\83F\83N\83g
+        * @throws MemberWalkerException
+        */
+       public void walk(Object t) throws MemberWalkerException {
+               if (t == null)
+                       throw new NullPointerException("Cannot walk about null object.");
+
+               if (t.getClass().isPrimitive())
+                       return;
+
+               // Class#getFields() \82Å\82Í public \83t\83B\81[\83\8b\83h\82µ\82©\8eQ\8fÆ\82Å\82«\82È\82¢\82Ì\82Å
+               // \83N\83\89\83X\82Ì\8aK\91w\82ð Object \83N\83\89\83X\82Ü\82Å\82½\82Ç\82Á\82Ä\83t\83B\81[\83\8b\83h\82Ì\83G\83\93\83R\81[\83h\8f\88\97\9d\82ð\8ds\82¤\81D
+               for (Class<?> cls = t.getClass();; cls = cls.getSuperclass()) {
+                       for (Method method : cls.getDeclaredMethods()) {
+                               if (!wantMember(t, method))
+                                       continue;
+                               method.setAccessible(true); // public \88È\8aO\82Ì\83\81\83\\83b\83h\82É\82à\83A\83N\83Z\83X\8fo\97\88\82é\82æ\82¤\82É\82·\82é\81D
+                               handler.processMethod(t, method);
+                       }
+                       for (Field field : cls.getDeclaredFields()) {
+                               if (!wantMember(t, field))
+                                       continue;
+                               field.setAccessible(true); // public \88È\8aO\82Ì\83t\83B\81[\83\8b\83h\82É\82à\83A\83N\83Z\83X\8fo\97\88\82é\82æ\82¤\82É\82·\82é\81D
+                               handler.processField(t, field);
+                       }
+                       if (cls == Object.class)
+                               break;
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalkerAdapter.java b/src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalkerAdapter.java
new file mode 100644 (file)
index 0000000..a746f24
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+The MIT License
+
+Copyright (c) 2010 Takeyuki Nagao
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+package jp.sourceforge.dvibrowser.dvi2epub.reflect;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * {@link MemberWalkerHandler} \82Ì\82½\82ß\82Ì\83A\83_\83v\83^\83N\83\89\83X\81D
+ * @author nagaotakeyuki@gmail.com
+ *
+ */
+
+public class MemberWalkerAdapter implements MemberWalkerHandler
+{
+       private boolean done;
+       
+       public boolean wantMember(Object o, Member member)
+       throws MemberWalkerException
+       {
+           int modifiers = member.getModifiers();
+           int modifiersToIgnore = Modifier.TRANSIENT;
+           return ((modifiers & modifiersToIgnore) == 0) && !done;
+       }
+       
+       public void setDone(boolean value) {
+               done = value;
+       }
+       
+       public boolean isDone() {
+               return done;
+       }
+
+  public void processField(Object t, Field field) throws MemberWalkerException {}
+
+  public void processMethod(Object t, Method method) throws MemberWalkerException {}
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalkerException.java b/src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalkerException.java
new file mode 100644 (file)
index 0000000..b0364af
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+The MIT License
+
+Copyright (c) 2010 Takeyuki Nagao
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+package jp.sourceforge.dvibrowser.dvi2epub.reflect;
+
+/**
+ * {@link MemberWalkerHandler} \83C\83\93\83^\81[\83t\83F\83C\83X\82Å\97á\8aO\82ð\83\89\83b\83v\82·\82é\82½\82ß\82Ì\97á\8aO\83N\83\89\83X\81D
+ * @author nagaotakeyuki@gmail.com
+ *
+ */
+public class MemberWalkerException
+extends Exception
+{
+  private static final long serialVersionUID = 7427816842687879124L;
+
+  public MemberWalkerException() {
+    super();
+  }
+
+  public MemberWalkerException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  public MemberWalkerException(String message) {
+    super(message);
+  }
+
+  public MemberWalkerException(Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalkerHandler.java b/src/jp/sourceforge/dvibrowser/dvi2epub/reflect/MemberWalkerHandler.java
new file mode 100644 (file)
index 0000000..9e2a953
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+The MIT License
+
+Copyright (c) 2010 Takeyuki Nagao
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+package jp.sourceforge.dvibrowser.dvi2epub.reflect;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+/**
+ * {@link MemberWalker} \83N\83\89\83X\82ª\8cÂ\81X\82Ì\83\81\83\93\83o\82É\91Î\82µ\82Ä\8ds\82¤
+ * \8f\88\97\9d\93à\97e\82ð\8bL\8fq\82·\82é\82½\82ß\82Ì\83C\83\93\83^\81[\83t\83F\83C\83X\81D
+ * 
+ * @author nagaotakeyuki@gmail.com
+ *
+ */
+public interface MemberWalkerHandler {
+  /**
+   * \93Á\92è\82Ì\83\81\83\93\83o\82ð {@link #processField(Object, Field)} \8by\82Ñ {@link #processMethod(Object, Method)}
+   * \82Ì\8cÄ\82Ñ\8fo\82µ\91Î\8fÛ\82Æ\82·\82é\82©\82Ç\82¤\82©\82ð\8c\88\92è\82·\82é\83\81\83\\83b\83h
+   * @param t \91Î\8fÛ\83I\83u\83W\83F\83N\83g
+   * @param member \91Î\8fÛ\83I\83u\83W\83F\83N\83g\82Ì\83\81\83\93\83o\81i\83t\83B\81[\83\8b\83h\82à\82µ\82­\82Í\83\81\83\\83b\83h\81j
+   * @return <code>member</code> \82Å\8ew\92è\82³\82ê\82½\83\81\83\93\83o\82ð\8cÄ\82Ñ\8fo\82µ\91Î\8fÛ\82Æ\82·\82é\8fê\8d\87\82É <code>true</code>\81D
+   * \82»\82ê\88È\8aO\82Í <code>false</code>
+   * @throws MemberWalkerException
+   */
+  boolean wantMember(Object t, Member member) throws MemberWalkerException;
+
+  /**
+   * \83t\83B\81[\83\8b\83h\82ª\8c©\82Â\82©\82Á\82½\8fê\8d\87\82É\8cÄ\82Ñ\8fo\82³\82ê\82é\83\81\83\\83b\83h\81D
+   * @param t \91Î\8fÛ\83I\83u\83W\83F\83N\83g
+   * @param field \8c©\82Â\82©\82Á\82½\83t\83B\81[\83\8b\83h
+   * @throws MemberWalkerException
+   */
+  void processField(Object t, Field field) throws MemberWalkerException;
+
+  /**
+   * \83\81\83\\83b\83h\82ª\8c©\82Â\82©\82Á\82½\8fê\8d\87\82É\8cÄ\82Ñ\8fo\82³\82ê\82é\83\81\83\\83b\83h\81D
+   * @param t \91Î\8fÛ\83I\83u\83W\83F\83N\83g
+   * @param method \8c©\82Â\82©\82Á\82½\83\81\83\\83b\83h
+   * @throws MemberWalkerException
+   */
+  void processMethod(Object t, Method method) throws MemberWalkerException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviByteRange.java b/src/jp/sourceforge/dvibrowser/dvicore/DviByteRange.java
new file mode 100644 (file)
index 0000000..9ad4ef8
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+/**
+ * An immutable class to represent a range of bytes.
+ * @author Takeyuki Nagao
+ *
+ */
+
+public class DviByteRange
+{
+       public static final DviByteRange EMPTY = new DviByteRange();
+
+  private final long begin;
+  private final long end;
+
+       private DviByteRange()
+       {
+         begin = 0;
+               end = -1;
+       }
+
+       public DviByteRange(long begin, long end)
+       {
+         if (begin <= end) {
+           this.begin = begin;
+                 this.end = end;
+               } else {
+           this.begin = end;
+                 this.end = begin;
+               }
+       }
+
+       public long begin()
+       {
+         return begin;
+       }
+       
+       public long end()
+       {
+         return end;
+       }
+
+       public DviByteRange translate(long by)
+       {
+         return new DviByteRange(begin + by, end + by);
+       }
+
+       public DviByteRange intersect(DviByteRange a)
+       {
+         if (a == null) return this;
+         if (this.isEmpty()) return EMPTY;
+               if (a.isEmpty()) return EMPTY;
+               long b = Math.max(this.begin, a.begin);
+               long e = Math.min(this.end, a.end);
+               return (b > e) ? EMPTY : new DviByteRange(b, e);
+       }
+
+       public DviByteRange union(DviByteRange a)
+       {
+         if (a == null) return this;
+         if (this.isEmpty()) return a;
+               if (a.isEmpty()) return this;
+               return new DviByteRange(
+                 Math.min(this.begin, a.begin),
+                 Math.max(this.end, a.end)
+               );
+       }
+
+       public boolean isEmpty()
+       {
+         return this == EMPTY;
+       }
+
+       public long length()
+       {
+         return end - begin + 1;
+       }
+
+       public boolean contains(long point)
+       {
+         return (begin <= point && point <= end);
+       }
+
+       public boolean intersects(DviByteRange a)
+       {
+         if (a == null) return false;
+               return !this.intersect(a).isEmpty();
+       }
+
+       public String toString() {
+         return "" + begin + "--" + end;
+       }
+       public int hashCode() {
+         return (int)(begin + 33*end);
+       }
+
+       public boolean equals(Object obj) {
+         if (obj instanceof DviByteRange) {
+                 DviByteRange br = (DviByteRange) obj;
+                       return br.begin == begin && br.end == end;
+               }
+               return false;
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviColor.java b/src/jp/sourceforge/dvibrowser/dvicore/DviColor.java
new file mode 100644 (file)
index 0000000..e156690
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+// immutable.
+
+public final class DviColor
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = 5615669030601265281L;
+
+  public static final DviColor INVALID = new DviColor();
+
+  private final int r, g, b;
+  private final int intRGB;
+  private final boolean valid;
+  
+  public int getRed() { return r;}
+  public int getGreen() { return g;}
+  public int getBlue() { return b;}
+  
+  public Color toColor()
+  {
+    return new Color(r, g, b);
+  }
+  
+  public DviColor() {
+    r = g = b = intRGB = 0;
+    valid = false;
+  }
+
+  public DviColor(int rgb)
+  {
+    this(
+      (rgb >>> 16) & 0xff,
+      (rgb >>> 8) & 0xff,
+      rgb & 0xff
+    );
+  }
+
+  public DviColor(int r, int g, int b)
+  {
+    if (0 <= r && r < 256 && 0 <= g && g < 256 && 0 <= b && b < 256) {
+      this.r = r;
+      this.g = g;
+      this.b = b;
+      intRGB = (r << 16) | (g << 8) | b;
+      valid = true;
+    } else {
+      throw new IllegalArgumentException
+        ("r=" + r + " g=" + g + " b=" + b);
+    }
+  }
+
+  public boolean isValid()
+  {
+    return valid;
+  }
+
+  public static DviColor fromIntRGB(int v)
+  {
+    final int r, g, b;
+
+    b = v & 0xff;   v >>>= 8;
+    g = v & 0xff;   v >>>= 8;
+    r = v & 0xff;
+
+    return RGB(r, g, b);
+  }
+
+  public static DviColor RGB(int r, int g, int b)
+  {
+    return new DviColor(r, g, b);
+  }
+
+  public static DviColor RGB(double r, double g, double b)
+  {
+    return RGB(
+      (int)(r * 255 + 0.5),
+      (int)(g * 255 + 0.5),
+      (int)(b * 255 + 0.5)
+    );
+  }
+
+  public static DviColor HSV(int h, int s, int v)
+  {
+    if (s == 0) {
+      return RGB(v, v, v);
+    }
+    
+    h = ((h % 360) + 360) % 360; // h = h mod 360
+    final double f = h % 60;
+    
+    final int p = (int)((v*(255.0 - s) + 255.0/2.0) / 255.0);
+    final int q = (int)((v*(60.0*255.0 - f * s) + 60.0*255.0 / 2.0) / (60.0 * 255.0));
+    final int t = (int)((v*(60.0*255.0 - (60.0 - f) * s) + 60.0*255.0 / 2.0) / (60.0 * 255.0));
+    
+    int r, g, b;
+    
+    switch (h / 60) {
+    case 0: r=v; g=t; b=p; break;
+    case 1: r=q; g=v; b=p; break;
+    case 2: r=p; g=v; b=t; break;
+    case 3: r=p; g=q; b=v; break;
+    case 4: r=t; g=p; b=v; break;
+    case 5: r=v; g=p; b=q; break;
+    default: throw new InternalError();
+    }
+    return RGB(r, g, b);
+  }
+
+  public static DviColor CMYK(double c, double m, double y, double k)
+  {
+    return RGB(
+      Math.max(0.0, 1.0 - c - k),
+      Math.max(0.0, 1.0 - m - k),
+      Math.max(0.0, 1.0 - y - k)
+    );
+  }
+
+  public int toIntRGB()
+  {
+    return intRGB;
+  }
+
+  private static final Pattern rgbPat
+    = Pattern.compile(
+        "rgb\\s+([.0-9]+)\\s+([.0-9]+)\\s+([.0-9]+)",
+        Pattern.CASE_INSENSITIVE
+      );
+  private static final Pattern hsbPat
+    = Pattern.compile(
+        "hsb\\s+([.0-9]+)\\s+([.0-9]+)\\s+([.0-9]+)",
+        Pattern.CASE_INSENSITIVE
+      );
+  private static final Pattern cmykPat
+    = Pattern.compile(
+        "cmyk\\s+([.0-9]+)\\s+([.0-9]+)\\s+([.0-9]+)\\s+([.0-9]+)",
+        Pattern.CASE_INSENSITIVE
+      );
+  private static final Pattern grayPat
+    = Pattern.compile(
+        "gray\\s+([.0-9]+)",
+        Pattern.CASE_INSENSITIVE
+      );
+  
+  private static final HashMap<String, DviColor> aliases
+    = new HashMap<String, DviColor>();
+
+  static {
+    aliases.put(  "black", fromIntRGB(0x00000000));
+    aliases.put(   "blue", fromIntRGB(0x000000ff));
+    aliases.put(  "green", fromIntRGB(0x0000ff00));
+    aliases.put(   "cyan", fromIntRGB(0x0000ffff));
+    aliases.put(    "red", fromIntRGB(0x00ff0000));
+    aliases.put("magenta", fromIntRGB(0x00ff00ff));
+    aliases.put( "yellow", fromIntRGB(0x00ffff00));
+    aliases.put(  "white", fromIntRGB(0x00ffffff));
+    // TODO: support colors used by dvips.
+  }
+
+  public static DviColor parseColor(String str)
+  throws DviException
+  {
+    str = str.trim();
+    Matcher mat;
+
+    try {
+      if ((mat = rgbPat.matcher(str)).matches()) {
+        double r = Double.parseDouble(mat.group(1));
+        double g = Double.parseDouble(mat.group(2));
+        double b = Double.parseDouble(mat.group(3));
+        return RGB(r, g, b);
+      } else if ((mat = hsbPat.matcher(str)).matches()) {
+        double h = Double.parseDouble(mat.group(1));
+        double s = Double.parseDouble(mat.group(2));
+        double b = Double.parseDouble(mat.group(3));
+        return HSV(
+          (int)(h*359 + 0.5),
+          (int)(s*255 + 0.5),
+          (int)(b*255 + 0.5)
+        );
+      } else if ((mat = cmykPat.matcher(str)).matches()) {
+        double c = Double.parseDouble(mat.group(1));
+        double m = Double.parseDouble(mat.group(2));
+        double y = Double.parseDouble(mat.group(3));
+        double k = Double.parseDouble(mat.group(3));
+        return CMYK(c, m, y, k);
+      } else if ((mat = grayPat.matcher(str)).matches()) {
+        double v = Double.parseDouble(mat.group(1));
+        return RGB(v, v, v);
+      } else {
+        DviColor c = aliases.get(str.toLowerCase());
+        if (c != null) return c;
+        throw new DviException
+          ("unrecognized color specification: " + str);
+      }
+    } catch(NumberFormatException ex) {
+      throw new DviException(ex);
+    }
+  }
+
+  public int hashCode()
+  {
+    return r + 33*(g + 33*b);
+  }
+
+  public boolean equals(Object obj)
+  {
+    if (obj instanceof DviColor) {
+      DviColor c = (DviColor) obj;
+      return (r == c.r && g == c.g && b == c.b);
+    }
+    return false;
+  }
+
+  public String toString()
+  {
+    return getClass().getName() + "[hex=#"
+           + toHex(r) + toHex(g) + toHex(b) + "]";
+  }
+
+  private static String toHex(int a)
+  {
+    return (a < 0x10) ? "0" + Integer.toHexString(a)
+                      : Integer.toHexString(a);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviConstants.java b/src/jp/sourceforge/dvibrowser/dvicore/DviConstants.java
new file mode 100644 (file)
index 0000000..181e93d
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+public final class DviConstants
+{
+  private DviConstants() {} // disable instantiation
+  
+  public static final int            VF_ID_BYTE = 202;
+  public static final int         VF_SHORT_CHAR = 0;
+  public static final int          VF_LONG_CHAR = 242;
+
+  public static final int       TFM_ID_YOKOGUMI = 11;
+  public static final int       TFM_ID_TATEGUMI = 9;
+
+  public static final int           DVI_ID_BYTE = 2;
+  public static final int  DVI_ID_BYTE_TATEGUMI = 3;
+  public static final int           DVI_TRAILER = 223;
+
+  public static final double        MM_PER_INCH = 25.4;
+  public static final double     POINT_PER_INCH = 72.27;
+  public static final int           DEFAULT_NUM = 25400000;
+  public static final int           DEFAULT_DEN = 7227 * 65536;
+  public static final int           DEFAULT_MAG = 1000;
+
+  public static final int UNDEFINED_FONT_NUMBER = -1;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviException.java b/src/jp/sourceforge/dvibrowser/dvicore/DviException.java
new file mode 100644 (file)
index 0000000..6cb3310
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+public class DviException
+extends java.lang.Exception
+{
+  private static final long serialVersionUID = 2904774705739419493L;
+  
+  public DviException() {
+    super();
+  }
+  public DviException(String msg) {
+    super(msg);
+  }
+  public DviException(String msg, Throwable cause) {
+    super(msg, cause);
+  }
+  public DviException(Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviFontName.java b/src/jp/sourceforge/dvibrowser/dvicore/DviFontName.java
new file mode 100644 (file)
index 0000000..1038bf0
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+import java.io.Serializable;
+
+import jp.sourceforge.dvibrowser.dvicore.util.Canonicalizer;
+import jp.sourceforge.dvibrowser.dvicore.util.SimpleCanonicalizer;
+
+
+// immutable.
+
+public final class DviFontName
+implements Serializable
+{
+  private static final long serialVersionUID = 5473736840728986859L;
+  private final int al, nl;
+  private final String name;
+  private final int hash;
+
+  private DviFontName(int al, int nl, String name)
+  {
+    if (name == null)
+      throw new NullPointerException
+        ("name can't be null");
+    if (al < 0 || 255 < al)
+      throw new IllegalArgumentException
+        ("illegal value of area length: " + al);
+    if (nl < 0 || 255 < nl)
+      throw new IllegalArgumentException
+        ("illegal value of name length: " + nl);
+    if (name.length() != (al + nl))
+      throw new IllegalArgumentException
+        ("name size mismatch:"
+        + " name.length()=" + name.length()
+        + " al=" + al
+        + " nl=" + nl
+        );
+
+    this.al = al;
+    this.nl = nl;
+    this.name = name;
+    this.hash = al + 33*(nl + 33*name.hashCode());
+  }
+
+  public int areaLength() { return al; }
+  public int nameLength() { return nl; }
+  public String name() { return name; }
+
+  private static final Canonicalizer<DviFontName> canonicalizer
+    = new SimpleCanonicalizer<DviFontName>();
+
+  public static DviFontName getInstance(int al, int nl, String name)
+  {
+    return canonicalizer.canonicalize(
+      new DviFontName(al, nl, name)
+    );
+  }
+  public static DviFontName getInstance(int nl, String name)
+  {
+    return getInstance(0, nl, name);
+  }
+  public static DviFontName getInstance(String name)
+  {
+    return getInstance(0, name.length(), name);
+  }
+
+  private volatile String string = null;
+  public String toString() {
+    if (string == null) {
+      string = getClass().getName()
+        + "[al=" + al
+        + ",nl=" + nl
+        + ",name=" + name
+        + "]";
+    }
+    return string;
+  }
+
+  public boolean equals(Object obj) {
+    if (obj == this) return true;
+    if (obj instanceof DviFontName) {
+      DviFontName fn = (DviFontName) obj;
+      return name.equals(fn.name) && al == fn.al && nl == fn.nl;
+    }
+    return false;
+  }
+
+  public int hashCode() {
+    return hash;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviFontSpec.java b/src/jp/sourceforge/dvibrowser/dvicore/DviFontSpec.java
new file mode 100644 (file)
index 0000000..f08c56a
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+import jp.sourceforge.dvibrowser.dvicore.util.Canonicalizer;
+import jp.sourceforge.dvibrowser.dvicore.util.SimpleCanonicalizer;
+
+// immutable.
+
+public final class DviFontSpec
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = 2374998771511821276L;
+  private final int cs;          // check sum.
+  private final int ss;          // Space size.
+  private final int ds;          // Design size.
+  private final DviFontName fontName;
+
+  private final double dviPerTfmw;
+  private final int hash; // QUESTION: Should we make this transient?
+
+  private DviFontSpec(int cs, int ss, int ds, DviFontName fontName) {
+    this.cs = cs;
+    this.ss = ss;
+    this.ds = ds;
+    this.fontName = fontName;
+    this.hash = (
+      cs + 33*(ss + 33*(ds + 33*fontName.hashCode()))
+    );
+    dviPerTfmw = ss / (double) (1 << 20);
+  }
+
+  private static final Canonicalizer<DviFontSpec> canonicalizer
+    = new SimpleCanonicalizer<DviFontSpec>();
+  public static DviFontSpec getInstance(
+    int cs, int ss, int ds, DviFontName fontName)
+  {
+    return canonicalizer.canonicalize(
+      new DviFontSpec(cs, ss, ds, fontName)
+    );
+  }
+
+  public static DviFontSpec getInstance(
+    int cs, int ss, int ds, int al, int nl, byte [] name)
+  {
+    return getInstance(cs, ss, ds,
+      DviFontName.getInstance(al, nl, new String(name))
+    );
+  }
+
+  public static DviFontSpec getInstance(
+    int cs, int ss, int ds, int al, int nl, String name)
+  {
+    return getInstance(cs, ss, ds,
+      DviFontName.getInstance(al, nl, name)
+    );
+  }
+
+  public int checkSum()    { return cs; }
+  public int spaceSize()   { return ss; }
+  public int designSize()  { return ds; }
+  public int areaLength()  { return fontName.areaLength(); }
+  public int nameLength()  { return fontName.nameLength(); }
+  public DviFontName fontName() { return fontName; }
+  public String name() { return fontName.name(); }
+
+  public int tfmToDvi(int tfmw) {
+    return (int)(dviPerTfmw * tfmw);
+  }
+
+  private volatile String string = null;
+  public String toString() {
+    if (string == null) {
+      string = "FontSpec"
+             + "[cs=" + cs + ",ss=" + ss + ",ds=" + ds
+             + ",fontName=\"" + fontName + "\"]"
+             ;
+    }
+    return string;
+  }
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj instanceof DviFontSpec) {
+      DviFontSpec fs = (DviFontSpec) obj;
+      return (isCompatibleWith(fs) && (cs == fs.cs));
+    }
+    return false;
+  }
+  public boolean isCompatibleWith(DviFontSpec fs) {
+    return (
+             (fs != null)
+             && (ss == fs.ss) && (ds == fs.ds)
+             && fontName.equals(fs.fontName)
+           );
+  }
+
+  public int hashCode() {
+    return hash;
+  }
+
+  public DviFontSpec rename(String newName)
+  {
+    return getInstance(cs, ss, ds,
+        DviFontName.getInstance(newName)
+      );
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviFontTable.java b/src/jp/sourceforge/dvibrowser/dvicore/DviFontTable.java
new file mode 100644 (file)
index 0000000..60defb2
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class DviFontTable
+extends ConcurrentHashMap<Integer, DviFontSpec>
+{
+  private static final long serialVersionUID = -5082474565659155765L;
+
+  public DviFontTable() {
+    super();
+  }
+
+  public DviFontTable transformForVirtualFont(DviFontSpec parent, int vfDesignSize)
+  {
+    DviFontTable ft = new DviFontTable();
+
+    for (int fn : keySet()) {
+      DviFontSpec fs = get(fn);
+      ft.put(
+        fn,
+        DviFontSpec.getInstance(
+          fs.checkSum(),
+          (int)(
+            parent.spaceSize() * (double) fs.spaceSize()
+            / (double)(1 << 20)
+          ),
+          (int)(
+            parent.designSize() * (double) fs.designSize()
+            / (double) vfDesignSize
+          ),
+          fs.fontName()
+        )
+      );
+    }
+
+    return ft;
+  }
+
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    for (int fn : new TreeSet<Integer>(keySet())) {
+      DviFontSpec fs = (DviFontSpec) get(fn);
+      sb.append("fn[" + fn + "]=" + ((fs != null) ? fs.toString() : "null"));
+    }
+    return sb.toString();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviObject.java b/src/jp/sourceforge/dvibrowser/dvicore/DviObject.java
new file mode 100644 (file)
index 0000000..8503529
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+import jp.sourceforge.dvibrowser.dvicore.api.DviContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+
+
+public class DviObject
+implements DviContextSupport, DviSerialized
+{
+  private final DviContextSupport dcs;
+  private final String cacheKey;
+  private final DviUniqueId uniqueId;
+  
+  public DviObject(final DviContextSupport dcs)
+  {
+    this.dcs = dcs;
+    if (dcs == null)
+      throw new IllegalArgumentException
+        ("DviObject cannot be instantiated with null DviContextSupport.");
+    uniqueId = DviUniqueId.newInstance();
+    cacheKey = uniqueId.toString();
+  }
+  
+  public DviContext getDviContext()
+  {
+    return dcs.getDviContext();
+  }
+  
+  public String getCacheKey()
+  {
+    return cacheKey;
+  }
+  
+  public long getSerialNumber()
+  {
+    return uniqueId.getSerialNumber();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviPaperSize.java b/src/jp/sourceforge/dvibrowser/dvicore/DviPaperSize.java
new file mode 100644 (file)
index 0000000..4e4a180
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+// immutable.
+
+public class DviPaperSize
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = 4593375448502371177L;
+  public static final DviPaperSize UNBOUNDED = new DviPaperSize(Double.MAX_VALUE, Double.MAX_VALUE, "unbounded");
+  public static final DviPaperSize CROP_TO_WIDTH = new DviPaperSize(Double.MAX_VALUE, Double.MAX_VALUE, "crop-to-width");
+  public static final DviPaperSize CROP_TO_HEIGHT = new DviPaperSize(Double.MAX_VALUE, Double.MAX_VALUE, "crop-to-height");
+  public static final DviPaperSize CROP_TO_BOTH = new DviPaperSize(Double.MAX_VALUE, Double.MAX_VALUE, "crop-to-both");
+  public static final DviPaperSize FALLBACK = new DviPaperSize(210.0, 297.0, "A4(fallback)");
+;
+  private final double w_mm;
+  private final double h_mm;
+  private final String desc;
+
+  public DviPaperSize(double w_mm, double h_mm, String desc) {
+    this.w_mm = w_mm;
+    this.h_mm = h_mm;
+    this.desc = desc;
+  }
+
+  public double widthInMM() { return w_mm; }
+  public double heightInMM() { return h_mm; }
+
+  public int widthInDots(DviResolution res) {
+    return (int) Math.ceil(res.actualDpi() * w_mm / DviConstants.MM_PER_INCH);
+  }
+  public int heightInDots(DviResolution res) {
+    return (int) Math.ceil(res.actualDpi() * h_mm / DviConstants.MM_PER_INCH);
+  }
+  
+  public DviRect toBoundingBox(DviResolution res)
+  {
+    return new DviRect
+    (0, 0, widthInDots(res), heightInDots(res));
+  }
+
+  public String description() { return desc; }
+
+  public String toString() {
+    return "PaperSize"
+           + "{" + w_mm + "mm x " + h_mm + " mm"
+           + " desc=" + desc;
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof DviPaperSize) {
+      DviPaperSize a = (DviPaperSize) obj;
+      return a.w_mm == w_mm
+          && a.h_mm == h_mm
+          && desc.equals(a.desc)
+          ;
+    }
+    return false;
+  }
+
+  public int hashCode() {
+    return (int)(desc.hashCode() + 33*(w_mm + 33*h_mm));
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviPoint.java b/src/jp/sourceforge/dvibrowser/dvicore/DviPoint.java
new file mode 100644 (file)
index 0000000..f659269
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+import java.awt.Point;
+
+// immutable.
+
+public final class DviPoint
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = 5621339308791022419L;
+
+  public static final DviPoint ORIGIN = new DviPoint(0,0);
+
+  public final int x;
+  public final int y;
+
+  public DviPoint(int x, int y) {
+    this.x = x;
+    this.y = y;
+  }
+
+  public DviPoint(DviPoint orig) {
+    this(orig.x, orig.y);
+  }
+
+  public DviPoint translate(DviPoint p) {
+    return new DviPoint(x + p.x, y + p.y);
+  }
+  public DviPoint translate(int dx, int dy) {
+    return new DviPoint(x + dx, y + dy);
+  }
+
+  public DviPoint shrink(int f) {
+    if (f <= 0)
+      throw new IllegalArgumentException
+        ("shrink factor can't be <= 0.");
+
+    return new DviPoint(
+      flooredDivision(x, f),
+      flooredDivision(y, f)
+    );
+  }
+
+  public DviPoint magnify(int f) {
+    return new DviPoint(x * f, y * f);
+  }
+
+  private static int flooredDivision(int a, int b) {
+    return (a < 0) ? (a - b + 1) / b : a / b;
+  }
+
+
+  public int hashCode() {
+    return x + 33*y;
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof DviPoint) {
+      DviPoint p = (DviPoint) obj;
+      return p.x == x && p.y == y;
+    }
+    return false;
+  }
+
+  public String toString() {
+    return "(" + x + "," + y + ")";
+  }
+
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviRect.java b/src/jp/sourceforge/dvibrowser/dvicore/DviRect.java
new file mode 100644 (file)
index 0000000..660820c
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+import java.awt.Rectangle;
+
+// immutable.
+
+public final class DviRect
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = 1112844763203879499L;
+  public static final DviRect EMPTY = new DviRect();
+  private final int x;
+  private final int y;
+  private final int width;
+  private final int height;
+
+  public DviRect() {
+    this.x = 0;
+    this.y = 0;
+    this.width = 0;
+    this.height = 0;
+  }
+
+  public DviRect(int x, int y, int width, int height) {
+    if (width < 0) {
+      this.x = x + width;
+      this.width = -width;
+    } else {
+      this.x = x;
+      this.width = width;
+    }
+    if (height < 0) {
+      this.y = y + height;
+      this.height = -height;
+    } else {
+      this.y = y;
+      this.height = height;
+    }
+  }
+
+  public DviRect(DviPoint p, int width, int height) {
+    this(p.x, p.y, width, height);
+  }
+
+  public DviRect(DviPoint p, DviSize s) {
+    this(p.x, p.y, s.width(), s.height());
+  }
+  
+  public DviRect(int x, int y, DviSize s) {
+    this(x, y, s.width(), s.height());
+  }
+
+  public DviRect(DviRect r) {
+    this(r.x(), r.y(), r.width(), r.height());
+  }
+
+  public boolean isEmpty() {
+    return width == 0 || height == 0;
+  }
+
+  public boolean contains(int x, int y) {
+    return this.x <= x && x < this.x + width &&
+           this.y <= y && y < this.y + height;
+  }
+
+  public boolean contains(DviPoint p) {
+    return this.x <= p.x && p.x < this.x + width &&
+           this.y <= p.y && p.y < this.y + height;
+  }
+
+  public DviSize size() {
+    return new DviSize(width, height);
+  }
+
+  public DviRect shrink(int f) {
+    if (f <= 0)
+      throw new IllegalArgumentException("shrink factor can't be <= 0.");
+
+    DviRect r = getDviRect(
+      flooredDivision(x, f),
+      flooredDivision(y, f),
+      flooredDivision(right(), f),
+      flooredDivision(bottom(), f));
+
+    return r;
+  }
+
+  public DviRect magnify(int f) {
+    return new DviRect(x * f, y * f, width * f, height * f);
+  }
+
+  public static DviRect getDviRect(int sx, int sy, int ex, int ey) {
+    return new DviRect(sx, sy, ex - sx + 1, ey - sy + 1);
+  }
+
+  public DviRect translate(DviPoint p) {
+    return new DviRect(x + p.x, y + p.y, width, height);
+  }
+  public DviRect translate(int dx, int dy) {
+    return new DviRect(x + dx, y + dy, width, height);
+  }
+  
+  public DviRect moveTo(int x, int y) {
+    return new DviRect(x, y, width, height);
+  }
+
+  public DviRect crop(int top, int left, int bottom, int right)
+  {
+    return new DviRect(
+      x + left,
+      y + top,
+      width - left - right,
+      height - top - bottom
+    );
+  }
+
+  public DviRect union(DviRect r) {
+    if (r == null) r = EMPTY;
+    if (isEmpty()) return r;
+    if (r.isEmpty()) return this;
+    int sx = Math.min(x, r.x);
+    int sy = Math.min(y, r.y);
+    int ex = Math.max(right(), r.right());
+    int ey = Math.max(bottom(), r.bottom());
+    return getDviRect(sx, sy, ex, ey);
+  }
+  
+  public static DviRect union(DviRect [] rs) {
+    DviRect ret = EMPTY;
+    if (rs != null) {
+      for (DviRect r : rs) {
+        ret = ret.union(r);
+      }
+    }
+    return ret;
+  }
+
+  public DviRect intersect(DviRect r) {
+    int sx = Math.max(x, r.x);
+    int sy = Math.max(y, r.y);
+    int ex = Math.min(right(), r.right());
+    int ey = Math.min(bottom(), r.bottom());
+    return (sx <= ex && sy <= ey) ? getDviRect(sx, sy, ex, ey)
+      : new DviRect();
+  }
+
+  public boolean intersects(DviRect r) {
+    int sx = Math.max(x, r.x);
+    int sy = Math.max(y, r.y);
+    int ex = Math.min(right(), r.right());
+    int ey = Math.min(bottom(), r.bottom());
+    return (sx < ex && sy < ey);
+  }
+
+  public DviRect addPadding(int by) {
+    if (isEmpty()) return this;
+    return getDviRect(x - by, y - by, right() + by, bottom() + by);
+  }
+
+  private static int flooredDivision(final int a, final int b) {
+    return (a < 0) ? (a - b + 1) / b : a / b;
+  }
+
+  public int x() { return x; }
+  public int y() { return y; }
+  public int width() { return width; }
+  public int height() { return height; }
+  public int left() { return x; }
+  public int top() { return y; }
+  public int right() { return x + width - 1; }
+  public int bottom() { return y + height - 1; }
+
+  public DviPoint topLeft() {
+    return new DviPoint(x, y);
+  }
+  public DviPoint topRight() {
+    return new DviPoint(x+width-1, y);
+  }
+  public DviPoint bottomLeft() {
+    return new DviPoint(x, y+height-1);
+  }
+  public DviPoint bottomRight() {
+    return new DviPoint(x+width-1, y+height-1);
+  }
+  
+  public DviRect resize(int width, int height)
+  {
+    return new DviRect(this.x, this.y, width, height);
+  }
+
+  public int hashCode() {
+    return (x + 33*(y + 33*(width + 33*height)));
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof DviRect) {
+      DviRect a = (DviRect) obj;
+      return isEmpty() ? a.isEmpty() : (
+        x == a.x && y == a.y &&
+        width == a.width && height == a.height
+      );
+    } return false;
+  }
+
+  public String toString() {
+    return getClass().getName()
+          +"[x=" + x
+          +",y=" + y
+          +",width=" + width
+          +",height=" + height
+          +"]";
+  }
+  
+  public Rectangle toRectangle()
+  {
+    return new Rectangle(x, y, width - 1, height - 1);
+  }
+  
+  public static DviRect fromRectangle(Rectangle rect)
+  {
+    if (rect == null) return null;
+    return new DviRect(rect.x, rect.y, rect.width + 1, rect.height + 1);
+  }
+
+  public DviRect magnify(double hScale, double vScale) {
+    return new DviRect((int) (x * hScale + 0.5), (int) (y * vScale + 0.5), size().magnify(hScale, vScale));
+  }
+  
+  public DviRect magnify(double scale) {
+    return magnify(scale, scale);
+  }
+
+  public DviRect moveTo(DviPoint point) {
+    if (point == null) throw new IllegalArgumentException("point can't be null");
+    return moveTo(point.x, point.y);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviRectSplitter.java b/src/jp/sourceforge/dvibrowser/dvicore/DviRectSplitter.java
new file mode 100644 (file)
index 0000000..307ec62
--- /dev/null
@@ -0,0 +1,88 @@
+package jp.sourceforge.dvibrowser.dvicore;
+
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+public class DviRectSplitter {
+  private final DviRect rect;
+  private final DviSize unit;
+  private final int cols;
+  private final int rows;
+
+  public DviRectSplitter(DviRect rect, DviSize unit)
+  {
+    this.rect = rect;
+    this.unit = unit;
+    
+    if (unit.width == 0) throw new IllegalArgumentException("width must be > 0: " + unit.width);
+    if (unit.height == 0) throw new IllegalArgumentException("height must be > 0: " + unit.height);
+    this.cols = (rect.width() + unit.width - 1) / unit.width;
+    this.rows = (rect.height() + unit.height - 1) / unit.height;
+  }
+  
+  public int getNumColumns()
+  {
+    return cols;
+  }
+  
+  public int getNumRows()
+  {
+    return rows;
+  }
+  
+  public DviRect getRect() { return rect; }
+  public DviSize getUnitSize() { return unit; }
+  
+  public DviRect getRectAt(int row, int col)
+  {
+    if (row < 0 || rows <= row)
+      throw new IllegalArgumentException("Row index out of range: " + row);
+    if (col < 0 || cols <= col)
+      throw new IllegalArgumentException("Column index out of range: " + col);
+    int x = rect.x() + col * unit.width();
+    int y = rect.y() + row * unit.height();
+    DviRect r = new DviRect(x, y, unit);
+    
+    return r.intersect(rect);
+  }
+  
+  public DviRect [][] getRects()
+  {
+    DviRect [] [] rects = new DviRect[rows][cols];
+    for (int i=0; i<rows; i++) {
+      for (int j=0; j<cols; j++) {
+        rects[i][j] = getRectAt(i, j);
+      }
+    }
+    return rects;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviRegister.java b/src/jp/sourceforge/dvibrowser/dvicore/DviRegister.java
new file mode 100644 (file)
index 0000000..e98436c
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+// mutable.
+
+public final class DviRegister
+implements Cloneable, java.io.Serializable
+{
+  private static final long serialVersionUID = 7923032642831406085L;
+  private int h, v, w, x, y, z;
+
+  public DviRegister() {
+  }
+
+  public DviRegister(DviRegister reg) {
+    this.h = reg.h;
+    this.v = reg.v;
+    this.w = reg.w;
+    this.x = reg.x;
+    this.y = reg.y;
+    this.z = reg.z;
+  }
+
+  public void copy(DviRegister reg) {
+    this.h = reg.h;
+    this.v = reg.v;
+    this.w = reg.w;
+    this.x = reg.x;
+    this.y = reg.y;
+    this.z = reg.z;
+  }
+
+  public int getH() { return h; }
+  public int getV() { return v; }
+  public int getW() { return w; }
+  public int getX() { return x; }
+  public int getY() { return y; }
+  public int getZ() { return z; }
+
+  public void setH(int h) { this.h = h; }
+  public void setV(int v) { this.v = v; }
+  public void setW(int w) { this.w = w; }
+  public void setX(int x) { this.x = x; }
+  public void setY(int y) { this.y = y; }
+  public void setZ(int z) { this.z = z; }
+
+  public void addH(int d) { h += d; }
+  public void addV(int d) { v += d; }
+  public void addW(int d) { w += d; }
+  public void addX(int d) { x += d; }
+  public void addY(int d) { y += d; }
+  public void addZ(int d) { z += d; }
+
+  public void reset() {
+    h = v = w = x = y = z = 0;
+  }
+
+  public String toString() {
+    return getClass().getName() + "[h=" + h +
+                      " v=" + v +
+                      " w=" + w +
+                      " x=" + x +
+                      " y=" + y +
+                      " z=" + z + "]";
+  }
+  public Object clone() {
+    try {
+      return super.clone();
+    } catch (CloneNotSupportedException ex) {
+      throw new InternalError();
+    }
+  }
+  public boolean equals(Object obj) {
+    if (obj instanceof DviRegister) {
+      DviRegister a = (DviRegister) obj;
+      return a.h == h && a.v == v && a.w == w &&
+             a.x == x && a.y == y && a.z == z;
+    }
+    return false;
+  }
+  public int hashCode() {
+    return h + 33*(v + 33*(w + 33*(x + 33*(y + 33*z))));
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviResolution.java b/src/jp/sourceforge/dvibrowser/dvicore/DviResolution.java
new file mode 100644 (file)
index 0000000..0e472d9
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+// immutable.
+
+public final class DviResolution
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = -8353020586758660972L;
+  public static final int MAX_SHRINK_FACTOR = 1024;
+
+  private final int dpi;
+  private final int shrinkFactor;
+  private final double actualDpi;
+
+  public DviResolution(int dpi, int shrinkFactor) {
+    this.dpi = dpi;
+    this.shrinkFactor = shrinkFactor;
+    checkVars();
+    actualDpi = (double) dpi / shrinkFactor;
+  }
+
+  private void checkVars() {
+    if (shrinkFactor <= 0 || MAX_SHRINK_FACTOR < shrinkFactor)
+      throw new IllegalArgumentException
+        ("Illegal value of shrinkFactor: " + shrinkFactor);
+  }
+
+  public double actualDpi() {
+    return actualDpi;
+  }
+  public int dpi() { return dpi; }
+  public int shrinkFactor() { return shrinkFactor; }
+
+  public DviResolution approximate(double approximateDpi) {
+    int sf = (int) Math.floor(dpi / approximateDpi);
+    sf = Math.min(sf, MAX_SHRINK_FACTOR);
+    sf = Math.max(sf, 1);
+    return new DviResolution(dpi, sf);
+  }
+
+  public String toString() {
+    return String.format(getClass().getName() + "[dpi=%d,shrinkFactor=%d,actualDpi=%.1f]", dpi, shrinkFactor, actualDpi());
+  }
+  public boolean equals(Object obj) {
+    if (obj instanceof DviResolution) {
+      DviResolution a = (DviResolution) obj;
+      return a.dpi == dpi && a.shrinkFactor == shrinkFactor;
+    }
+    return false;
+  }
+
+  public int hashCode() {
+    return dpi + 33*shrinkFactor;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviSerialized.java b/src/jp/sourceforge/dvibrowser/dvicore/DviSerialized.java
new file mode 100644 (file)
index 0000000..4a38416
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+public interface DviSerialized {
+  long getSerialNumber();
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviSize.java b/src/jp/sourceforge/dvibrowser/dvicore/DviSize.java
new file mode 100644 (file)
index 0000000..c19546d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+public final class DviSize
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = 2774228595468843394L;
+  public final int width;
+  public final int height;
+
+  public DviSize(int width, int height)
+  {
+    this.width = width;
+    this.height = height;
+  }
+
+  public int width() { return width; }
+  public int height() { return height; }
+
+  public String toString() {
+    return getClass().getName() + "[width=" + width + ",height=" + height + "]";
+  }
+
+  public int hashCode() {
+    return width + 33*height;
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof DviSize) {
+      DviSize a = (DviSize) obj;
+      return a.width == width && a.height == height;
+    }
+    return false;
+  }
+
+  public DviSize magnify(double scale) {
+    return new DviSize((int)(width * scale + 0.5), (int)(height * scale + 0.5));
+  }
+
+  public DviSize magnify(double hScale, double vScale) {
+    return new DviSize((int) (width * hScale + 0.5), (int) (height * vScale + 0.5));
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviUniqueId.java b/src/jp/sourceforge/dvibrowser/dvicore/DviUniqueId.java
new file mode 100644 (file)
index 0000000..1893e24
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+import java.util.UUID;
+
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+public final class DviUniqueId {
+  private final long serial;
+
+  private DviUniqueId()
+  {
+    serial = DviUtils.generateSerialNumber();
+  }
+  
+  public static DviUniqueId newInstance()
+  {
+    return new DviUniqueId();
+  }
+  
+  public String toString() {
+    return DviUtils.getApplicationInstanceUUID() + "--" + serial;
+  }
+  
+  public long getSerialNumber() {
+    return serial;
+  }
+  
+  public UUID getApplicationInstanceUUID() {
+    return DviUtils.getApplicationInstanceUUID();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/DviUnit.java b/src/jp/sourceforge/dvibrowser/dvicore/DviUnit.java
new file mode 100644 (file)
index 0000000..3b378ce
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore;
+
+import jp.sourceforge.dvibrowser.dvicore.util.Canonicalizer;
+import jp.sourceforge.dvibrowser.dvicore.util.SimpleCanonicalizer;
+
+// immutable
+
+public final class DviUnit
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = -3040236351321035760L;
+
+  public static final DviUnit DEFAULT;
+
+  private static final Canonicalizer<DviUnit> canonicalizer;
+  static {
+    canonicalizer = new SimpleCanonicalizer<DviUnit>();
+    DEFAULT = canonicalizer.canonicalize(new DviUnit());
+  }
+
+  private final int num, den, mag;
+  private final double inchPerDvi, pointPerDvi;
+  private final int hash;
+
+  private DviUnit() {
+    this.num = DviConstants.DEFAULT_NUM;
+    this.den = DviConstants.DEFAULT_DEN;
+    this.mag = DviConstants.DEFAULT_MAG;
+    inchPerDvi = (num/254000.0)/den*(mag/1000.0);
+    pointPerDvi = (num/254000.0)/(den/DviConstants.POINT_PER_INCH)*(mag/1000.0);
+    hash = hashCodeInternal();
+  }
+
+  private DviUnit(int num, int den, int mag) {
+    this.num = num;
+    this.den = den;
+    this.mag = mag;
+    checkVars();
+    inchPerDvi = (num/254000.0)/den*(mag/1000.0);
+    pointPerDvi = (num/254000.0)/(den/DviConstants.POINT_PER_INCH)*(mag/1000.0);
+    hash = hashCodeInternal();
+  }
+
+  public static DviUnit getInstance(int num, int den, int mag)
+  {
+    return canonicalizer.canonicalize(
+      new DviUnit(num, den, mag)
+    );
+  }
+
+  private int hashCodeInternal() {
+    return num + 33*(den + 33*mag);
+  }
+
+  public int numerator()     { return num; }
+  public int denominator()   { return den; }
+  public int magnification() { return mag; }
+
+  public double inchPerDvi() {
+    return inchPerDvi;
+  }
+  public double pointPerDvi() {
+    return pointPerDvi;
+  }
+  public double dotPerDvi(int dpi) {
+    return inchPerDvi * dpi;
+  }
+  public double mmPerDvi(int dpi) {
+    return inchPerDvi * DviConstants.MM_PER_INCH;
+  }
+
+  public int mapToPixel(int a, int dpi) {
+    return (int) mapToPixelDouble(a, dpi);
+  }
+  public int mapToPixel(int a, DviResolution res) {
+    return (int) mapToPixelDouble(a, res);
+  }
+
+  public double factorDouble(int dpi) {
+    return dpi * inchPerDvi;
+  }
+  public double factorDouble(DviResolution res) {
+    return res.actualDpi() * inchPerDvi;
+  }
+  public double mapToPixelDouble(int a, int dpi) {
+    return (dpi * inchPerDvi * a + 0.5);
+  }
+  public double mapToPixelDouble(int a, DviResolution res) {
+    return (res.actualDpi() * inchPerDvi * a + 0.5);
+  }
+
+  private void checkVars() {
+    if (num <= 0 || den <= 0 || mag <= 0)
+      throw new IllegalArgumentException
+        ("Invalid values of num/den*mag");
+  }
+
+  private volatile String string = null;
+  public String toString() {
+    if (string == null) {
+      string = getClass().getName() + "[" + num + "/" + den + "*" + mag + "]";
+    }
+    return string;
+  }
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj instanceof DviUnit) {
+      DviUnit a = (DviUnit) obj;
+      return (a.num == num && a.den == den && a.mag == mag);
+    }
+    return false;
+  }
+  public int hashCode() {
+    return hash;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/MetafontMode.java b/src/jp/sourceforge/dvibrowser/dvicore/MetafontMode.java
new file mode 100644 (file)
index 0000000..5dfd17d
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+package jp.sourceforge.dvibrowser.dvicore;
+
+// TOOD: implement the default values.
+public final class MetafontMode {
+  public static final MetafontMode FALLBACK_600DPI = new MetafontMode("ljfour", 600);
+  public static final MetafontMode FALLBACK_400DPI = new MetafontMode("agfatfzz", 400);
+  public static final MetafontMode FALLBACK = FALLBACK_600DPI;
+  private final String mode;
+  private final int bdpi;
+
+  public MetafontMode(String mode, int bdpi)
+  {
+    this.mode = mode;
+    this.bdpi = bdpi;
+  }
+
+  public String getMode() {
+    return mode;
+  }
+
+  public int getBdpi() {
+    return bdpi;
+  }
+  
+  public int hashCode()
+  {
+    return mode.hashCode() + 33 * bdpi;
+  }
+  
+  public boolean equals(Object o)
+  {
+    if (!(o instanceof MetafontMode)) {
+      return false;
+    }
+    MetafontMode mm = (MetafontMode) o;
+    return (mm.mode.equals(mode) && mm.bdpi == mm.bdpi);
+  }
+  
+  public String toString() {
+    return getClass().getName() + "[mode=" + mode + ",bdpi=" + bdpi + "]";
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/BinaryDevice.java b/src/jp/sourceforge/dvibrowser/dvicore/api/BinaryDevice.java
new file mode 100644 (file)
index 0000000..061c572
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+
+public interface BinaryDevice
+extends Device
+{
+  void begin() throws DviException;
+  void end() throws DviException;
+  boolean beginRaster(int w, int h) throws DviException;
+  void endRaster() throws DviException;
+  void beginLine() throws DviException;
+  void endLine(int repeat) throws DviException;
+  void putBits(int count, boolean paintFlag) throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/CharacterCodeMapper.java b/src/jp/sourceforge/dvibrowser/dvicore/api/CharacterCodeMapper.java
new file mode 100644 (file)
index 0000000..0f88f6f
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.font.LogicalFont;
+
+public interface CharacterCodeMapper {
+  public String mapCharacterCodeToUnicode
+    (LogicalFont logicalFont, int code) throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/Device.java b/src/jp/sourceforge/dvibrowser/dvicore/api/Device.java
new file mode 100644 (file)
index 0000000..13bee43
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviColor;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviPoint;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+
+//TODO: add BackgroundColor
+public interface Device
+{
+  DviResolution getResolution() throws DviException;
+
+  DviPoint getReferencePoint() throws DviException;
+  void setReferencePoint(DviPoint point) throws DviException;
+  void translate(int dx, int dy) throws DviException;
+  void translate(DviPoint p) throws DviException;
+
+  void setColor(DviColor color) throws DviException;
+  DviColor getColor() throws DviException;
+
+  void save() throws DviException;
+  void restore() throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DevicePainter.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DevicePainter.java
new file mode 100644 (file)
index 0000000..8754cc9
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+
+public interface DevicePainter
+{
+  void setOutput(BinaryDevice out) throws DviException;
+
+  void begin(GeometerContext ctx) throws DviException;
+  void end() throws DviException;
+
+  void beginPage(DviBop bop) throws DviException;
+  void endPage() throws DviException;
+
+  void beginFont(DviFontSpec fs) throws DviException;
+  void endFont() throws DviException;
+
+  void drawChar(int code) throws DviException;
+  void drawRule(int width, int height) throws DviException;
+  void drawSpecial(byte [] xxx) throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DviCacheable.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DviCacheable.java
new file mode 100644 (file)
index 0000000..8ce32d2
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+public interface DviCacheable {
+  public String getCacheKey();
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DviContext.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DviContext.java
new file mode 100644 (file)
index 0000000..918dac1
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Map;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviPaperSize;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.MetafontMode;
+import jp.sourceforge.dvibrowser.dvicore.ctx.DviToolkit;
+import jp.sourceforge.dvibrowser.dvicore.font.LogicalFont;
+import jp.sourceforge.dvibrowser.dvicore.util.progress.ProgressRecorder;
+
+
+public interface DviContext
+extends DviExecutor, DviContextSupport
+{
+  DviResolution getDefaultResolution() throws DviException;
+  MetafontMode getDefaultMetafontMode() throws DviException;
+  
+  DviPaperSize getDefaultPaperSize() throws DviException;
+  DviPaperSize findPaperSizeByName(String name) throws DviException;
+  DviPaperSize [] listPaperSizes() throws DviException;
+  
+  URL getDviResource(String filename) throws DviException;
+  
+  File getCacheDirectory() throws DviException;
+  File getTemporaryDirectory() throws DviException;
+  String getExecutableName(String name) throws DviException;
+  
+  Map<String, Glyph> getGlyphCache() throws DviException;
+  CharacterCodeMapper getCharacterCodeMapper(LogicalFont logicalFont) throws DviException;
+  DviExecutor newDviExecutor() throws DviException;
+  DevicePainter newDevicePainter() throws DviException;
+  DviDocument openDviDocument(File file) throws DviException;
+  DviDocument openDviDocument(URL url) throws DviException;
+  DviDocument openDviDocument(InputStream is) throws DviException;
+  DviFont findDviFont(LogicalFont logicalFont) throws DviException;
+  FullMetrics findDviFullMetrics(DviFontSpec fontSpec) throws DviException;
+  SimpleMetrics findDviSimpleMetrics(DviFontSpec fontSpec) throws DviException;
+  LogicalFont mapLogicalFont(LogicalFont logicalFont) throws DviException;
+  
+  ProgressRecorder getProgressRecorder();
+  DviToolkit getDviToolkit();
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DviContextSupport.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DviContextSupport.java
new file mode 100644 (file)
index 0000000..e94a4f3
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+public interface DviContextSupport {
+  public DviContext getDviContext();
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DviData.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DviData.java
new file mode 100644 (file)
index 0000000..35ffbb1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontTable;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+
+public interface DviData
+{
+  DviUnit getDviUnit() throws DviException;
+  DviFontTable getFontTable() throws DviException;
+  DviInput getInput() throws DviException;
+  long getDataSize() throws DviException; // optional
+  DviInput getInput(long start, long end) throws DviException; // optional
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DviDocument.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DviDocument.java
new file mode 100644 (file)
index 0000000..c3af35d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+
+public interface DviDocument
+extends DviData, DviCacheable, DviContextSupport
+{
+  DviPreamble getPreamble() throws DviException;
+  DviPostamble getPostamble() throws DviException;
+  DviPostPost getPostPost() throws DviException;
+  int getTotalPages() throws DviException;
+  DviPage [] getPages() throws DviException;
+  DviPage getPage(int p) throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DviExecutor.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DviExecutor.java
new file mode 100644 (file)
index 0000000..fbe65a2
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+
+public interface DviExecutor
+{
+  void execute(DviData data, DviExecutorHandler handler)
+    throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DviExecutorContext.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DviExecutorContext.java
new file mode 100644 (file)
index 0000000..5b1c764
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviByteRange;
+
+public interface DviExecutorContext
+extends DviContextSupport
+{
+  DviData getData();
+  int getCommand();
+  DviByteRange getCommandRange();
+  void setTerminate(boolean terminate);
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DviExecutorHandler.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DviExecutorHandler.java
new file mode 100644 (file)
index 0000000..48d9d27
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+
+
+public interface DviExecutorHandler
+{
+  void begin(DviExecutorContext ctx) throws DviException;
+  void end() throws DviException;
+
+  void doSet(int code) throws DviException;
+  void doSetRule(int w, int h) throws DviException;
+  void doPut(int code) throws DviException;
+  void doPutRule(int w, int h) throws DviException;
+  void doNop() throws DviException;
+
+  void doSelectFont(int fn) throws DviException;
+  void doDefineFont(int fn, DviFontSpec fs) throws DviException;
+
+  void doPush() throws DviException;
+  void doPop() throws DviException;
+
+  void doPre(DviPreamble preamble) throws DviException;
+  void doBop(DviBop bop) throws DviException;
+  void doEop() throws DviException;
+  void doPost(DviPostamble postamble) throws DviException;
+  void doPostPost(DviPostPost postPost) throws DviException;
+
+  void doRight(int by) throws DviException;
+  void doW(int by) throws DviException;
+  void doW0() throws DviException;
+  void doX(int by) throws DviException;
+  void doX0() throws DviException;
+
+  void doDown(int by) throws DviException;
+  void doY(int by) throws DviException;
+  void doY0() throws DviException;
+  void doZ(int by) throws DviException;
+  void doZ0() throws DviException;
+
+  void doSpecial(byte [] xxx) throws DviException;
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DviFont.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DviFont.java
new file mode 100644 (file)
index 0000000..750a6c5
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.font.LogicalFont;
+
+public interface DviFont
+{
+  Glyph getGlyph(LogicalFont lf, int code) throws DviException;
+  boolean hasChar(int code) throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DviInput.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DviInput.java
new file mode 100644 (file)
index 0000000..da60f63
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import java.io.IOException;
+
+public interface DviInput
+{
+  int readU1() throws IOException;
+  int readU2() throws IOException;
+  int readU3() throws IOException;
+  int readU4() throws IOException;
+  int readU(int len) throws IOException;
+
+  int readS1() throws IOException;
+  int readS2() throws IOException;
+  int readS3() throws IOException;
+  int readS4() throws IOException;
+  int readS(int len) throws IOException;
+
+  void readFully(byte [] buf) throws IOException;
+
+  long getOffset();
+
+//  boolean ready() throws IOException;
+
+  void close() throws IOException;
+
+  void skip(int len) throws IOException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/DviPage.java b/src/jp/sourceforge/dvibrowser/dvicore/api/DviPage.java
new file mode 100644 (file)
index 0000000..b359117
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviByteRange;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+
+public interface DviPage
+extends DviData, DviCacheable, DviContextSupport
+{
+  int getPageNumber() throws DviException;
+  DviDocument getDocument() throws DviException;
+  DviByteRange range() throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/FullMetrics.java b/src/jp/sourceforge/dvibrowser/dvicore/api/FullMetrics.java
new file mode 100644 (file)
index 0000000..2b237af
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+
+public interface FullMetrics
+extends SimpleMetrics
+{
+  int getTfmHeight(int code) throws DviException;
+  int getTfmDepth(int code) throws DviException;
+  int getTfmItalic(int code) throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/GammaCorrector.java b/src/jp/sourceforge/dvibrowser/dvicore/api/GammaCorrector.java
new file mode 100644 (file)
index 0000000..4ca33cc
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+public interface GammaCorrector
+extends DviCacheable
+{
+  int correctGamma(int c, int maxval);
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/Geometer.java b/src/jp/sourceforge/dvibrowser/dvicore/api/Geometer.java
new file mode 100644 (file)
index 0000000..0eb706d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+public interface Geometer
+extends DviExecutorHandler
+{
+  void setPainter(DevicePainter dp);
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/GeometerContext.java b/src/jp/sourceforge/dvibrowser/dvicore/api/GeometerContext.java
new file mode 100644 (file)
index 0000000..9794a30
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviRegister;
+
+public interface GeometerContext {
+  DviRegister getRegister();
+  DviFontSpec currentFontSpec();
+  DviExecutorContext getExecuterContext();
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/Glyph.java b/src/jp/sourceforge/dvibrowser/dvicore/api/Glyph.java
new file mode 100644 (file)
index 0000000..67c2154
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+
+public interface Glyph
+{
+  DviRect bounds() throws DviException;
+  int width() throws DviException;
+  int height() throws DviException;
+  int xOffset() throws DviException;
+  int yOffset() throws DviException;
+  int xAdvance() throws DviException;
+  int yAdvance() throws DviException;
+  void rasterizeTo(BinaryDevice out) throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/HasURL.java b/src/jp/sourceforge/dvibrowser/dvicore/api/HasURL.java
new file mode 100644 (file)
index 0000000..078576d
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import java.net.URL;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+
+
+public interface HasURL {
+  public URL getURL() throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/ImageDevice.java b/src/jp/sourceforge/dvibrowser/dvicore/api/ImageDevice.java
new file mode 100644 (file)
index 0000000..0f4bcc4
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+
+public interface ImageDevice
+extends Device
+{
+  DviRect getBounds() throws DviException;
+  void begin(int maxval) throws DviException;
+  void end() throws DviException;
+  boolean beginImage(int w, int h) throws DviException;
+  void endImage() throws DviException;
+  void putLine(int [] buf, int off, int len) throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/api/SimpleMetrics.java b/src/jp/sourceforge/dvibrowser/dvicore/api/SimpleMetrics.java
new file mode 100644 (file)
index 0000000..dbfb76b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.api;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+
+public interface SimpleMetrics
+{
+  boolean hasChar(int code) throws DviException;
+  int getTfmWidth(int code) throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/cli/tools/ConvertToImage.java b/src/jp/sourceforge/dvibrowser/dvicore/cli/tools/ConvertToImage.java
new file mode 100644 (file)
index 0000000..89ff203
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+package jp.sourceforge.dvibrowser.dvicore.cli.tools;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.imageio.ImageIO;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviPaperSize;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.ctx.DefaultDviContext;
+import jp.sourceforge.dvibrowser.dvicore.gui.swing.ViewSpec;
+import jp.sourceforge.dvibrowser.dvicore.image.split.ImageFileConfig;
+import jp.sourceforge.dvibrowser.dvicore.util.Benchmark;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+import jp.sourceforge.dvibrowser.dvicore.util.ZipBuilder;
+
+
+public class ConvertToImage
+extends DviObject
+{
+  private static final String OPT_SHRINK_FACTOR = "--shrink-factor=";
+  private static final String OPT_DPI = "--dpi=";
+  private static final String OPT_OUTPUT_FILE = "--output-file=";
+  private static final String OPT_RESOURCES_FILE = "--resources-file=";
+  private static final String OPT_PAPER_SIZE = "--paper-size=";
+  private static final String OPT_USE_BBOX = "--use-bbox=";
+  private static final String OPT_PADDING = "--padding=";
+  private static final Logger LOGGER = Logger.getLogger(ConvertToImage.class
+      .getName());
+  
+  public static class Config
+  extends DviObject
+  {
+    private final ViewSpec viewSpec;
+    private final ArrayList<File> inputs = new ArrayList<File>();
+    private boolean useBoundingBox;
+    private File outputFile;
+    private DviPaperSize paperSize;
+    private ImageFileConfig imageFileConfig;
+    private int padding;
+       private File resourcesFile;
+    
+    public Config(DviContextSupport dcs) {
+      super(dcs);
+      viewSpec = new ViewSpec(dcs);
+      try {
+        setPaperSize(getDviContext().getDefaultPaperSize());
+      } catch (DviException e) {
+        DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+        setPaperSize(DviPaperSize.FALLBACK);
+      }
+      // TODO: outsource the config.
+      imageFileConfig = ImageFileConfig.PNG;
+      padding = 0;
+    }
+    
+    public void setDpi(int dpi)
+    {
+      viewSpec.setResolution(new DviResolution(dpi, viewSpec.getResolution().shrinkFactor()));
+    }
+    
+    public void setShrinkFactor(int sf)
+    {
+      viewSpec.setResolution(new DviResolution(viewSpec.getResolution().dpi(), sf));
+    }
+    
+    public ViewSpec getViewSpec() {
+      return viewSpec;
+    }
+    
+    public void setUseBoundingBox(boolean crop) {
+      this.useBoundingBox = crop;
+    }
+    
+    public boolean useBoundingBox() { return useBoundingBox; }
+    
+    public void setOutputFile(File outputFile) {
+      if (outputFile != null) {
+        this.outputFile = outputFile;
+      } else {
+        this.outputFile = null;
+      }
+    }
+    
+    public void addInputFile(File file)
+    {
+      if (file != null) {
+        inputs.add(file);
+      } else {
+        LOGGER.warning("Input file is null. Ignored.");
+      }
+    }
+
+    public File getOutputFile() {
+      return outputFile;
+    }
+    
+    public DviPaperSize getPaperSize() {
+      return paperSize;
+    }
+    
+
+    public ArrayList<File> getInputs() {
+      return inputs;
+    }
+    
+    public void parseArguments(String [] args) throws DviException
+    {
+      final DviContext ctx = getDviContext();
+      for (int i=0; i<args.length; i++) {
+        String a = args[i];
+        if (a.startsWith(OPT_DPI)) {
+          int dpi = Integer.parseInt(a.substring(OPT_DPI.length()));
+          setShrinkFactor(dpi);
+        } else if (a.startsWith(OPT_SHRINK_FACTOR)) {
+          int sf = Integer.parseInt(a.substring(OPT_SHRINK_FACTOR.length()));
+          setShrinkFactor(sf);
+        } else if (a.startsWith(OPT_PADDING)) {
+          int padding = Integer.parseInt(a.substring(OPT_PADDING.length()));
+          setPaddingSize(padding);
+        } else if (a.startsWith(OPT_PAPER_SIZE)) {
+          String s = a.substring(OPT_PAPER_SIZE.length());
+          DviPaperSize paperSize = ctx.findPaperSizeByName(s);
+          if (paperSize == null) {
+            throw new DviException("Unrecognized papersize: " + s);
+          }
+        } else if (a.startsWith(OPT_RESOURCES_FILE)) {
+            String s = a.substring(OPT_RESOURCES_FILE.length()).trim();
+            if (!"".equals(s)) {
+              setResourcesFile(new File(s));
+            } else {
+              throw new DviException("Output filename is empty.");
+            }
+        } else if (a.startsWith(OPT_OUTPUT_FILE)) {
+          String s = a.substring(OPT_OUTPUT_FILE.length()).trim();
+          if (!"".equals(s)) {
+            setOutputFile(new File(s));
+          } else {
+            throw new DviException("Output filename is empty.");
+          }
+        } else if (a.startsWith(OPT_USE_BBOX)) {
+          String s = a.substring(OPT_USE_BBOX.length()).trim();
+          if ("yes".equalsIgnoreCase(s)) {
+            setUseBoundingBox(true);
+          } else if ("no".equalsIgnoreCase(s)) {
+              setUseBoundingBox(false);
+          } else {
+            throw new DviException("Invalid parameter value: " + a);
+          }
+        } else if (a.startsWith("-")) {
+          throw new DviException("Unrecognized commandline option: " + a);
+        } else {
+          for (; i<args.length; i++) {
+            a = args[i];
+            addInputFile(new File(a));
+          }
+        }
+      }
+    }
+
+    private void setResourcesFile(File file) {
+       this.resourcesFile = file;
+       }
+
+       public File getResourcesFile() {
+               return resourcesFile;
+       }
+
+       private void setPaddingSize(int padding) {
+      this.setPadding(padding);
+    }
+
+    public void setPaperSize(DviPaperSize paperSize) {
+      this.paperSize = paperSize;
+    }
+    
+    public ImageFileConfig getImageFileConfig()
+    {
+      return imageFileConfig;
+    }
+
+    public void setImageFileConfig(ImageFileConfig imageFileConfig) {
+      this.imageFileConfig = imageFileConfig;
+    }
+    
+    public static String getUsage()
+    {
+      StringWriter sw = new StringWriter();
+      PrintWriter pw = new PrintWriter(sw);
+      pw.println("usage: java " + ConvertToImage.class.getName() + " [options] <input-files>");
+      pw.println("  options: ");
+      pw.println("    --dpi=N            Set output DPI to N");
+      pw.println("    --shrink-factor=N  Set shrink factor to N (1--1024)");
+      pw.println("    --output-file=F    Set output zip file to F");
+      pw.println("    --resources-file=F Set resources file to F");
+      pw.println("    --padding=N        Set padding size to N");
+      return sw.toString();
+    }
+    
+    public void showUsage()
+    {
+      System.err.println(getUsage());
+    }
+
+    public boolean isValid() {
+      return getInputs().size() > 0;
+    }
+
+    public void setPadding(int padding) {
+      this.padding = padding;
+    }
+
+    public int getPadding() {
+      return padding;
+    }
+  }
+  
+  public ConvertToImage(DviContextSupport dcs) {
+    super(dcs);
+  }
+
+  public int convert(Config config) throws Exception {
+    DviContext ctx = getDviContext();
+    ViewSpec vs = config.getViewSpec();
+    DviResolution res = vs.getResolution();
+    File outputFile = config.getOutputFile();
+    OutputStream os = null;
+    if (outputFile != null) {
+      if (!outputFile.getName().toLowerCase().endsWith(".zip")) {
+        outputFile = new File(outputFile.getParentFile(), outputFile.getName() + ".zip");
+      }
+      os = new FileOutputStream(outputFile);
+      LOGGER.info("Writing outputs to " + outputFile);
+    } else {
+      os = new FilterOutputStream(System.out) {
+        @Override
+        public void close()
+        {
+          // We don't close the stdout.
+        }
+      };
+    }
+    ZipBuilder zb = new ZipBuilder(os);
+    
+    try {
+      for (File file : config.getInputs()) {
+        try {
+          DviDocument doc = ctx.openDviDocument(file);
+          LOGGER.info("Processing file: " + file);
+          for (int i=0; i<doc.getTotalPages(); i++) {
+            DviPage page = doc.getPage(i);
+            LOGGER.info("Processing page " + (i + 1) + "/" + doc.getTotalPages());
+            DviRect bbox;
+            if (config.useBoundingBox()) {
+              bbox = 
+                ctx.getDviToolkit().computeBoundingBox(page, res);
+            } else {
+              bbox = config.getPaperSize().toBoundingBox(res);
+            }
+            bbox = bbox.addPadding(config.getPadding());
+            
+            BufferedImage img = ctx.getDviToolkit().renderToBufferedImage(page, bbox, vs);
+            if (img == null) {
+              throw new DviException("Failed to render page " + (i + 1));
+            }
+            
+            OutputStream imageOut = zb.openOutputStream
+              (String.format("%04d%s", i+1, config.getImageFileConfig().getImageExtension()));
+            try {
+              ImageIO.write(img, config.getImageFileConfig().getImageType(), imageOut);
+            } finally {
+              DviUtils.silentClose(imageOut);
+            }
+          }
+        } catch (DviException e) {
+          DviUtils.logStackTrace(LOGGER, Level.SEVERE, e);
+          throw e;
+        }
+      }
+    } finally {
+      DviUtils.silentClose(zb);
+    }
+    return 0;
+  }
+
+  public static void main(String[] args)
+  {
+    try {
+      DefaultDviContext ctx = new DefaultDviContext();
+      
+      Config config = new Config(ctx);
+      config.parseArguments(args);
+      
+      if (!config.isValid()) {
+        config.showUsage();
+        throw new DviException
+          ("Unrecognized command line: " + DviUtils.join(" ", args));
+      }
+      
+      File resourcesFile = config.getResourcesFile();
+
+      if (resourcesFile != null) {
+         ctx.setRecordResources(true);
+      }
+      
+      Benchmark benchmark = new Benchmark();
+      benchmark.begin("dvi rendering");
+      benchmark.addSample();
+      ConvertToImage app = new ConvertToImage(ctx);
+      int retcode = app.convert(config);
+      benchmark.end();
+      
+      if (resourcesFile != null) {
+       Set<URL> resources = ctx.getRecordedResources();
+       FileOutputStream fos = new FileOutputStream(resourcesFile);
+       PrintWriter out = new PrintWriter(fos);
+       for (URL url : resources) {
+               out.println(url);
+       }
+       out.flush();
+       out.close();
+       fos.close();
+      }
+      
+      LOGGER.info("Finished: " + benchmark.format());
+      // INT_ARGB: Finished: Benchmark result: dvi rendering: 1 samples in 68.182 sec. 0.015 samples/sec.  68182 msec./sample.
+      // INT_RGB: Finished: Benchmark result: dvi rendering: 1 samples in 62.976 sec. 0.016 samples/sec.  62976 msec./sample.
+      System.exit(retcode);
+    } catch (Exception e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      System.exit(1);
+    }
+  }
+
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/cmd/DviBop.java b/src/jp/sourceforge/dvibrowser/dvicore/cmd/DviBop.java
new file mode 100644 (file)
index 0000000..2f37076
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.cmd;
+
+/**
+ * An immutable class to represent the BOP (Begin Of Page) command.
+ * This class is used internally to parse the DVI data.
+ * @author Takeyuki NAGAO (nagaotakeyuki@gmail.com)
+ */
+
+public class DviBop
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = 1326126283946043078L;
+  
+  private final int count[];
+  private final int backPointer;
+
+  public DviBop(int [] count, int backPointer) {
+    this.count = count.clone(); // a defensive copy
+    this.backPointer = backPointer;
+    checkValues();
+  }
+
+  public void checkValues() {
+    if (count.length != 10)
+      throw new IllegalArgumentException
+        ("count must be of type int [10].");
+  }
+
+  public String countAsString() {
+    int i, j;
+
+    StringBuilder sb = new StringBuilder();
+    sb.append(String.valueOf(count[0]));
+    for (i=count.length-1; i>0; i--) 
+      if (count[i] != 0) break;
+    for (j=1; j<=i; j++)
+      sb.append(":" + count[j]);
+    
+    return sb.toString();
+  }
+
+  public int [] count() { return count.clone(); }
+  public int backPointer() { return backPointer; }
+
+  public String toString() {
+    return getClass().getName() + "[count=" + countAsString() +
+           ",backPointer=" + backPointer + "]";
+  }
+
+  public boolean equals(Object o) {
+    if (o instanceof DviBop) {
+      DviBop a = (DviBop) o;
+      if (!(a.backPointer == backPointer
+          && a.count.length == count.length
+          && count.length == 10))
+        return false;
+
+      for (int i=0; i<10; i++)
+        if (a.count[i] != count[i])
+          return false;
+
+      return true;
+    }
+    return false;
+  }
+
+  public int hashCode() {
+    int hash = backPointer;
+    for (int a : count)
+      hash = a + 33 * hash;
+    return hash;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/cmd/DviCommand.java b/src/jp/sourceforge/dvibrowser/dvicore/cmd/DviCommand.java
new file mode 100644 (file)
index 0000000..1647b82
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.cmd;
+
+public class DviCommand
+{
+  private DviCommand() {} // To disable instantiation.
+  
+  public static final int DVI_SET1 = 128;
+  public static final int DVI_SET2 = 129;
+  public static final int DVI_SET3 = 130;
+  public static final int DVI_SET4 = 131;
+
+  public static final int DVI_SET_RULE = 132;
+
+  public static final int DVI_PUT1 = 133;
+  public static final int DVI_PUT2 = 134;
+  public static final int DVI_PUT3 = 135;
+  public static final int DVI_PUT4 = 136;
+
+  public static final int DVI_PUT_RULE = 137;
+
+  public static final int DVI_NOP = 138;
+  public static final int DVI_BOP = 139;
+  public static final int DVI_EOP = 140;
+  public static final int DVI_PUSH = 141;
+  public static final int DVI_POP = 142;
+
+  public static final int DVI_RIGHT1 = 143;
+  public static final int DVI_RIGHT2 = 144;
+  public static final int DVI_RIGHT3 = 145;
+  public static final int DVI_RIGHT4 = 146;
+
+  public static final int DVI_DOWN1 = 157;
+  public static final int DVI_DOWN2 = 158;
+  public static final int DVI_DOWN3 = 159;
+  public static final int DVI_DOWN4 = 160;
+
+
+  public static final int DVI_W0 = 147;
+  public static final int DVI_W1 = 148;
+  public static final int DVI_W2 = 149;
+  public static final int DVI_W3 = 150;
+  public static final int DVI_W4 = 151;
+
+  public static final int DVI_X0 = 152;
+  public static final int DVI_X1 = 153;
+  public static final int DVI_X2 = 154;
+  public static final int DVI_X3 = 155;
+  public static final int DVI_X4 = 156;
+
+  public static final int DVI_Y0 = 161;
+  public static final int DVI_Y1 = 162;
+  public static final int DVI_Y2 = 163;
+  public static final int DVI_Y3 = 164;
+  public static final int DVI_Y4 = 165;
+
+  public static final int DVI_Z0 = 166;
+  public static final int DVI_Z1 = 167;
+  public static final int DVI_Z2 = 168;
+  public static final int DVI_Z3 = 169;
+  public static final int DVI_Z4 = 170;
+
+  public static final int DVI_FONT1 = 235;
+  public static final int DVI_FONT2 = 236;
+  public static final int DVI_FONT3 = 237;
+  public static final int DVI_FONT4 = 238;
+
+  public static final int DVI_XXX1 = 239;
+  public static final int DVI_XXX2 = 240;
+  public static final int DVI_XXX3 = 241;
+  public static final int DVI_XXX4 = 242;
+                         
+  public static final int DVI_FNT_DEF1 = 243;
+  public static final int DVI_FNT_DEF2 = 244;
+  public static final int DVI_FNT_DEF3 = 245;
+  public static final int DVI_FNT_DEF4 = 246;
+
+  public static final int DVI_POST = 248;
+  public static final int DVI_POST_POST = 249;
+  public static final int DVI_PRE = 247;
+
+  public static final int DVI_UNDEF1 = 250;
+  public static final int DVI_UNDEF2 = 251;
+  public static final int DVI_UNDEF3 = 252;
+  public static final int DVI_UNDEF4 = 253;
+  public static final int DVI_UNDEF5 = 254;
+  public static final int DVI_UNDEF6 = 255;
+  public static final int DVI_TDIR = 255; /* For Japanese pTeX */
+
+  private static final String [] commandNames = {
+  "SETC_000","SETC_001","SETC_002","SETC_003","SETC_004",
+  "SETC_005","SETC_006","SETC_007","SETC_008","SETC_009",
+  "SETC_010","SETC_011","SETC_012","SETC_013","SETC_014",
+  "SETC_015","SETC_016","SETC_017","SETC_018","SETC_019",
+  "SETC_020","SETC_021","SETC_022","SETC_023","SETC_024",
+  "SETC_025","SETC_026","SETC_027","SETC_028","SETC_029",
+  "SETC_030","SETC_031","SETC_032","SETC_033","SETC_034",
+  "SETC_035","SETC_036","SETC_037","SETC_038","SETC_039",
+  "SETC_040","SETC_041","SETC_042","SETC_043","SETC_044",
+  "SETC_045","SETC_046","SETC_047","SETC_048","SETC_049",
+  "SETC_050","SETC_051","SETC_052","SETC_053","SETC_054",
+  "SETC_055","SETC_056","SETC_057","SETC_058","SETC_059",
+  "SETC_060","SETC_061","SETC_062","SETC_063","SETC_064",
+  "SETC_065","SETC_066","SETC_067","SETC_068","SETC_069",
+  "SETC_070","SETC_071","SETC_072","SETC_073","SETC_074",
+  "SETC_075","SETC_076","SETC_077","SETC_078","SETC_079",
+  "SETC_080","SETC_081","SETC_082","SETC_083","SETC_084",
+  "SETC_085","SETC_086","SETC_087","SETC_088","SETC_089",
+  "SETC_090","SETC_091","SETC_092","SETC_093","SETC_094",
+  "SETC_095","SETC_096","SETC_097","SETC_098","SETC_099",
+  "SETC_100","SETC_101","SETC_102","SETC_103","SETC_104",
+  "SETC_105","SETC_106","SETC_107","SETC_108","SETC_109",
+  "SETC_110","SETC_111","SETC_112","SETC_113","SETC_114",
+  "SETC_115","SETC_116","SETC_117","SETC_118","SETC_119",
+  "SETC_120","SETC_121","SETC_122","SETC_123","SETC_124",
+  "SETC_125","SETC_126","SETC_127",
+  "SET1","SET2","SET3","SET4","SET_RULE",
+  "PUT1","PUT2","PUT3","PUT4","PUT_RULE",
+  "NOP","BOP","EOP","PUSH","POP",
+  "RIGHT1","RIGHT2","RIGHT3","RIGHT4",
+  "W0","W1","W2","W3","W4",
+  "X0","X1","X2","X3","X4",
+  "DOWN1","DOWN2","DOWN3","DOWN4",
+  "Y0","Y1","Y2","Y3","Y4",
+  "Z0","Z1","Z2","Z3","Z4",
+  "FONT_00","FONT_01","FONT_02","FONT_03","FONT_04",
+  "FONT_05","FONT_06","FONT_07","FONT_08","FONT_09",
+  "FONT_10","FONT_11","FONT_12","FONT_13","FONT_14",
+  "FONT_15","FONT_16","FONT_17","FONT_18","FONT_19",
+  "FONT_20","FONT_21","FONT_22","FONT_23","FONT_24",
+  "FONT_25","FONT_26","FONT_27","FONT_28","FONT_29",
+  "FONT_30","FONT_31","FONT_32","FONT_33","FONT_34",
+  "FONT_35","FONT_36","FONT_37","FONT_38","FONT_39",
+  "FONT_40","FONT_41","FONT_42","FONT_43","FONT_44",
+  "FONT_45","FONT_46","FONT_47","FONT_48","FONT_49",
+  "FONT_50","FONT_51","FONT_52","FONT_53","FONT_54",
+  "FONT_55","FONT_56","FONT_57","FONT_58","FONT_59",
+  "FONT_60","FONT_61","FONT_62","FONT_63",
+  "FNT1","FNT2","FNT3","FNT4",
+  "XXX1","XXX2","XXX3","XXX4",
+  "FNT_DEF1","FNT_DEF2","FNT_DEF3","FNT_DEF4",
+  "PRE","POST","POST_POST",
+  "UNDEF_250","UNDEF_251","UNDEF_252","UNDEF_253","UNDEF_254","UNDEF_255"
+  };
+
+  public static String getName(int c) {
+    if (c < 0 || c > 255)
+      throw new IllegalArgumentException
+        ("no dvi command with code " + c + ".");
+    return commandNames[c];
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/cmd/DviPostPost.java b/src/jp/sourceforge/dvibrowser/dvicore/cmd/DviPostPost.java
new file mode 100644 (file)
index 0000000..fd6279b
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.cmd;
+
+// immutable.
+
+public class DviPostPost
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = 4094400368736425377L;
+  private final int postamblePointer;
+  private final int idByte;
+
+  public DviPostPost(int postamblePointer, int idByte) {
+    this.idByte = idByte;
+    this.postamblePointer = postamblePointer;
+    checkVars();
+  }
+
+  public void checkVars() {}
+
+  public int postamblePointer() { return postamblePointer; }
+  public int idByte() { return idByte; }
+
+  public String toString() {
+    return getClass().getName() + "[postamblePointer=" + postamblePointer +
+           ",idByte=" + idByte + "]";
+  }
+
+  public boolean equals(Object o) {
+    if (o instanceof DviPostPost) {
+      DviPostPost pp = (DviPostPost) o;
+      return pp.idByte == idByte
+          && pp.postamblePointer == pp.postamblePointer
+          ;
+    }
+    return false;
+  }
+
+  public int hashCode() {
+    return postamblePointer + 33*idByte;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/cmd/DviPostamble.java b/src/jp/sourceforge/dvibrowser/dvicore/cmd/DviPostamble.java
new file mode 100644 (file)
index 0000000..e98f72b
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.cmd;
+
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+
+// immutable.
+
+public class DviPostamble
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = 2018955950810232285L;
+  private final int firstBackPointer;
+  private final DviUnit dviUnit;
+  private final int maxV, maxH;
+  private final int maxStackDepth;
+  private final int totalPages;
+
+  public DviPostamble(int firstBackPointer, DviUnit dviUnit,
+    int maxV, int maxH, int maxStackDepth, int totalPages)
+  {
+    this.firstBackPointer = firstBackPointer;
+    this.dviUnit = dviUnit;
+    this.maxV = maxV;
+    this.maxH = maxH;
+    this.maxStackDepth = maxStackDepth;
+    this.totalPages = totalPages;
+    checkValues();
+  }
+
+  public void checkValues() {}
+
+  public int firstBackPointer() { return firstBackPointer; }
+  public DviUnit dviUnit()      { return dviUnit; }
+  public int maxV()             { return maxV; }
+  public int maxH()             { return maxH; }
+  public int maxStackDepth()    { return maxStackDepth; }
+  public int totalPages()       { return totalPages; }
+
+  public String toString() {
+    return getClass().getName() + "[firstBackPointer=" + firstBackPointer +
+               ",dviUnit=" + dviUnit +
+               ",max(H,V)=(" + maxH + "," + maxV + ")" +
+               ",maxStackDepth=" + maxStackDepth +
+               ",totalPages=" + totalPages + "]";
+  }
+
+  public boolean equals(Object o) {
+    if (o instanceof DviPostamble) {
+      DviPostamble p = (DviPostamble) o;
+      return p.firstBackPointer == firstBackPointer
+          && p.dviUnit.equals(dviUnit)
+          && p.maxV == maxV
+          && p.maxH == maxH
+          && p.maxStackDepth == maxStackDepth
+          && p.totalPages == totalPages
+          ;
+    }
+    return false;
+  }
+
+  public int hashCode() {
+    return firstBackPointer
+         + 33*(dviUnit.hashCode()
+         + 33*(maxV
+         + 33*(maxH
+         + 33*(maxStackDepth
+         + 33*totalPages
+         ))))
+         ;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/cmd/DviPreamble.java b/src/jp/sourceforge/dvibrowser/dvicore/cmd/DviPreamble.java
new file mode 100644 (file)
index 0000000..7458e4b
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.cmd;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+
+// immutable.
+
+public class DviPreamble
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = -455212635042527368L;
+  private final int idByte;
+  private final DviUnit dviUnit;
+  private final byte [] comment;
+
+  public DviPreamble(int idByte, DviUnit dviUnit, byte [] comment)
+    throws DviException
+  {
+    this.idByte  = idByte;
+    this.dviUnit = dviUnit;
+    this.comment = comment.clone();
+  }
+
+  public int idByte() { return idByte; }
+  public DviUnit dviUnit() { return dviUnit; }
+  public byte [] comment() { return comment.clone(); }
+
+  public String toString() {
+    return getClass().getName() + "[idByte=" + idByte + ",dviUnit=" + dviUnit +
+             ",comment=\"" + new String(comment) + "\"]";
+  }
+
+  public boolean equals(Object o) {
+    if (o instanceof DviPreamble) {
+      DviPreamble p = (DviPreamble) o;
+      return p.idByte == idByte
+          && p.dviUnit.equals(dviUnit)
+          && new String(p.comment).equals(new String(comment))
+          ;
+    }
+    return false;
+  }
+
+  public int hashCode() {
+    return idByte + 33*(dviUnit.hashCode()
+         + 33 * new String(comment).hashCode())
+         ;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/AbstractDviResourceResolver.java b/src/jp/sourceforge/dvibrowser/dvicore/ctx/AbstractDviResourceResolver.java
new file mode 100644 (file)
index 0000000..bee78d4
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.ctx;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.Computation;
+
+
+public abstract class AbstractDviResourceResolver<S, T>
+extends DviObject
+implements Computation<String, Collection<T>>
+{
+  private final S spec;
+  public AbstractDviResourceResolver(DviContextSupport dcs, S spec) {
+    super(dcs);
+    this.spec = spec;
+  }
+
+  public Collection<T> call() throws Exception
+  {
+    List<T> list = new ArrayList<T>();
+    String filename = mapToDviResourceName(getSpec());
+    URL url = getDviContext().getDviResource(filename);
+    if (url != null) {
+      final T item = createInstanceFromURL(url);
+      if (item != null) {
+        list.add(item);
+      }
+    }
+    return list;
+  }
+  
+  @Override
+  public String getCacheKey()
+  {
+    return mapToDviResourceName(getSpec());
+  }
+  
+  protected T createInstanceFromURL(URL url) throws DviException,
+      IOException
+  {
+    if (url == null) return null;
+    return createInstanceFromStream(url.openStream());
+  }
+
+  protected abstract T createInstanceFromStream(InputStream openStream) throws DviException;
+
+  protected abstract String mapToDviResourceName(S spec);
+
+  public S getSpec()
+  {
+    return spec;
+  }
+
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/AsyncComputers.java b/src/jp/sourceforge/dvibrowser/dvicore/ctx/AsyncComputers.java
new file mode 100644 (file)
index 0000000..da63ca8
--- /dev/null
@@ -0,0 +1,66 @@
+package jp.sourceforge.dvibrowser.dvicore.ctx;
+
+import java.net.URL;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Properties;
+
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+import jp.sourceforge.dvibrowser.dvicore.api.FullMetrics;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.CacheEntry;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.CachedComputer;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.ThreadedComputer;
+
+public class AsyncComputers {
+  private final CachedComputer<String, Collection<URL>> dviResourceComputer;
+  private final CachedComputer<String, Collection<DviFont>> dviFontComputer;
+  private final CachedComputer<String, Collection<FullMetrics>> fullMetricsComputer;
+  private final CachedComputer<String, DviRect> boundingBoxComputer;
+
+  protected final Properties properties;
+
+  public AsyncComputers(Properties prop) {
+    this.properties = prop;
+    this.dviResourceComputer = new CachedComputer<String, Collection<URL>>(
+        new ThreadedComputer<String, Collection<URL>>(1));
+    this.dviFontComputer = new CachedComputer<String, Collection<DviFont>>(
+        new ThreadedComputer<String, Collection<DviFont>>(1)) {
+      @Override
+      protected boolean removeEldestEntry(
+          Map.Entry<String, CacheEntry<String, Collection<DviFont>>> entry) {
+        boolean remove = getCache().size() > 64;
+        return remove;
+      }
+    };
+
+    fullMetricsComputer = new CachedComputer<String, Collection<FullMetrics>>(
+        new ThreadedComputer<String, Collection<FullMetrics>>(1));
+
+    boundingBoxComputer = new CachedComputer<String, DviRect>(
+        new ThreadedComputer<String, DviRect>(1)) {
+      @Override
+      protected boolean removeEldestEntry(
+          Map.Entry<String, CacheEntry<String, DviRect>> entry) {
+        return getCache().size() > 1024;
+      }
+    };
+
+  }
+
+  public CachedComputer<String, Collection<URL>> getDviResourceComputer() {
+    return dviResourceComputer;
+  }
+
+  public CachedComputer<String, Collection<DviFont>> getDviFontComputer() {
+    return dviFontComputer;
+  }
+
+  public CachedComputer<String, Collection<FullMetrics>> getFullMetricsComputer() {
+    return fullMetricsComputer;
+  }
+
+  public CachedComputer<String, DviRect> getBoundingBoxComputer() {
+    return boundingBoxComputer;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/BakomaUnicodeCharacterCodeMapper.java b/src/jp/sourceforge/dvibrowser/dvicore/ctx/BakomaUnicodeCharacterCodeMapper.java
new file mode 100644 (file)
index 0000000..efe9b16
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.ctx;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.CharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.font.LogicalFont;
+
+public class BakomaUnicodeCharacterCodeMapper implements CharacterCodeMapper {
+  public String mapCharacterCodeToUnicode(LogicalFont logicalFont, int codePoint)
+      throws DviException
+  {
+    if (0x00 <= codePoint && codePoint <= 0x09) {
+      codePoint += 161;
+    } else if (0x0a == codePoint) {
+      codePoint = 173;
+    } else if (0x14 == codePoint) {
+      // http://argent.shinshu-u.ac.jp/~otobe/tex/book/WinXP.html
+      codePoint = 0x2219;
+    } else if (0x0b <= codePoint && codePoint <= 0x20) {
+      codePoint += 174 - 0x0b;
+    } else if (0x7f == codePoint) {
+      codePoint = 196;
+    }
+    
+    if (Character.charCount(codePoint) == 1) {
+      return String.valueOf((char) codePoint);
+    } else {
+      return new String(Character.toChars(codePoint));
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/DefaultDviContext.java b/src/jp/sourceforge/dvibrowser/dvicore/ctx/DefaultDviContext.java
new file mode 100644 (file)
index 0000000..2c8c55f
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.ctx;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.AccessControlException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviPaperSize;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.MetafontMode;
+import jp.sourceforge.dvibrowser.dvicore.api.CharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.api.DevicePainter;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviData;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutor;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorHandler;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+import jp.sourceforge.dvibrowser.dvicore.api.FullMetrics;
+import jp.sourceforge.dvibrowser.dvicore.api.Glyph;
+import jp.sourceforge.dvibrowser.dvicore.api.SimpleMetrics;
+import jp.sourceforge.dvibrowser.dvicore.doc.DirectFileDviDocument;
+import jp.sourceforge.dvibrowser.dvicore.doc.StreamDviDocument;
+import jp.sourceforge.dvibrowser.dvicore.doc.URLDviDocument;
+import jp.sourceforge.dvibrowser.dvicore.font.DviFontResolver;
+import jp.sourceforge.dvibrowser.dvicore.font.FullMetricsResolver;
+import jp.sourceforge.dvibrowser.dvicore.font.LogicalFont;
+import jp.sourceforge.dvibrowser.dvicore.gs.GhostscriptUtils;
+import jp.sourceforge.dvibrowser.dvicore.render.BasicExecutor;
+import jp.sourceforge.dvibrowser.dvicore.render.DefaultDevicePainter;
+import jp.sourceforge.dvibrowser.dvicore.util.DviCache;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.Computation;
+import jp.sourceforge.dvibrowser.dvicore.util.progress.ManagedProgressItem;
+import jp.sourceforge.dvibrowser.dvicore.util.progress.ProgressRecorder;
+
+
+public class DefaultDviContext
+implements DviContext
+{
+  private static final Logger LOGGER = Logger.getLogger(DefaultDviContext.class.getName());
+  
+  private static final String DVICORE_INTERNAL_FILENAME_PREFIX = "dvicore-";
+  
+  private final Map<String, Glyph> glyphCache = new DviCache<String, Glyph>(16384);
+  private final CharacterCodeMapper characterCodeMapper
+    = new SimpleJisToUnicodeMapper();
+  
+  private boolean recordResources = false;
+  private final Set<URL> resources = new TreeSet<URL>(new Comparator<URL>() {
+       public int compare(URL arg0, URL arg1) {
+               return arg0.toString().compareTo(arg1.toString());
+       }
+  });
+  
+  private final ProgressRecorder recorder = new ProgressRecorder(this) {
+    @Override
+    protected boolean removeEldestElement(ManagedProgressItem item)
+    {
+      List<ManagedProgressItem> list = getProgressItems();
+      if (list.size() > 10) {
+        return true;
+      }
+      return false;
+    }
+  };
+
+  private AsyncComputers asyncComputers;
+
+
+  public DefaultDviContext()
+  throws DviException
+  {
+    this(null);
+  }
+
+  public DefaultDviContext(Properties prop)
+  throws DviException
+  {
+    if (prop == null)
+      prop = getDefaultProperties();
+    this.prop = prop;
+    initializeComputers();
+    initializeFontMapper();
+    populatePaperSizes();
+    initializeDirs();
+  }
+  
+  protected void initializeComputers() {
+       this.setAsyncComputers(new AsyncComputers(prop));
+  }
+  
+  protected void initializeDirs()
+  throws DviException
+  {
+    File cacheDir = getCacheDirectory();
+    if (cacheDir != null && !cacheDir.exists()) {
+      if (!cacheDir.mkdirs()) {
+        throw new DviException("Failed to create cache directory: " + cacheDir);
+      }
+    }
+    File tmpDir = getTemporaryDirectory();
+    if (tmpDir != null && !tmpDir.exists()) {
+      if (!tmpDir.mkdirs()) {
+        throw new DviException("Failed to create temporary directory: " + tmpDir);
+      }
+    }
+  }
+
+  protected Properties getDefaultProperties()
+  throws DviException
+  {
+    try {
+      URL url = DefaultDviContext.class.getResource("default-context.properties");
+      Properties prop = new Properties();
+      if (null != url) {
+          prop.load(url.openStream());
+      } else {
+         LOGGER.warning("Failed to load default-context.properties");
+      }
+      return prop;
+    } catch (Exception e) {
+      throw new DviException(e);
+    }
+  }
+  
+  private final Properties prop;
+
+  public Properties getProperties()
+  {
+    return prop;
+  }
+  
+  public void execute(DviData data, DviExecutorHandler handler)
+      throws DviException
+  {
+    newDviExecutor().execute(data, handler);
+  }
+
+// TODO: delete
+//  private static final CachedComputer<String, Collection<DviFont>> dviFontComputer
+//  = new CachedComputer<String, Collection<DviFont>>
+//    (new ThreadedComputer<String, Collection<DviFont>>(1)) {
+//    @Override
+//    protected boolean removeEldestEntry(Map.Entry<String, CacheEntry<String, Collection<DviFont>>> entry)
+//    {
+//      boolean remove = getCache().size() > 64;
+//      return remove;
+//    }
+//  };
+
+  public DviFont findDviFont(LogicalFont logicalFont) throws DviException
+  {
+    Computation<String, Collection<DviFont>> computation
+      = new DviFontResolver(this, logicalFont);
+    try {
+      Collection<DviFont> fonts = getAsyncComputers().getDviFontComputer()
+        .compute(computation).get();
+      for (DviFont font : fonts) {
+        return font;
+      }
+      return null;
+    } catch (InterruptedException e) {
+      throw new DviException(e);
+    } catch (ExecutionException e) {
+      throw new DviException(e);
+    }
+  }
+  
+  public SimpleMetrics findDviSimpleMetrics(DviFontSpec fs) throws DviException
+  {
+    return findDviFullMetrics(fs);
+  }
+
+//  private static final CachedComputer<String, Collection<FullMetrics>> fullMetricsComputer
+//  = new CachedComputer<String, Collection<FullMetrics>>
+//    (new ThreadedComputer<String, Collection<FullMetrics>>(1));
+
+  public FullMetrics findDviFullMetrics(DviFontSpec fs) throws DviException
+  {
+    Computation<String, Collection<FullMetrics>> computation = new FullMetricsResolver(
+        this, fs);
+    try {
+      Collection<FullMetrics> fonts = getAsyncComputers().getFullMetricsComputer()
+       .compute(computation).get();
+      if (fonts != null) {
+        for (FullMetrics font : fonts) {
+          return font;
+        }
+      }
+      return null;
+    } catch (InterruptedException e) {
+      throw new DviException(e);
+    } catch (ExecutionException e) {
+      throw new DviException(e);
+    }
+  }
+
+  public CharacterCodeMapper getCharacterCodeMapper(LogicalFont logicalFont) throws DviException
+  {
+    return characterCodeMapper;
+  }
+  
+  public DviContext getDviContext() { return this; }
+  
+  public Map<String, Glyph> getGlyphCache() throws DviException
+  {
+    return Collections.synchronizedMap(glyphCache);
+  }
+
+  protected void initializeFontMapper()
+  {
+  }
+  
+  public DevicePainter newDevicePainter() throws DviException
+  {
+    return new DefaultDevicePainter(this);
+  }
+
+  public DviExecutor newDviExecutor() throws DviException
+  {
+    return new BasicExecutor(this);
+  }
+  
+  public DviDocument openDviDocument(File file) throws DviException
+  {
+    return new DirectFileDviDocument(this, file);
+  }
+
+  public DviDocument openDviDocument(InputStream is) throws DviException
+  {
+    return new StreamDviDocument(this, is);
+  }
+
+  // TODO: implement async resource loading
+  public DviDocument openDviDocument(URL url) throws DviException
+  {
+    return new URLDviDocument(this, url);
+  }
+
+  public ProgressRecorder getProgressRecorder()
+  {
+    return recorder;
+  }
+
+  private static final Map<String, DviPaperSize> paperSizes = new TreeMap<String, DviPaperSize>(); 
+  
+  protected void populatePaperSizes()
+  {
+    // ISO 216 sizes
+    // TODO: implement B and C serieses. A4 Japanese, too.
+    // TOOD: outsource the configuration.
+    addPaperSize(new DviPaperSize(841.0, 1189.0, "A0"));
+    addPaperSize(new DviPaperSize(594.0, 841.0, "A1"));
+    addPaperSize(new DviPaperSize(420.0, 594.0, "A2"));
+    addPaperSize(new DviPaperSize(297.0, 420.0, "A3"));
+    addPaperSize(new DviPaperSize(210.0, 297.0, "A4"));
+    addPaperSize(new DviPaperSize(148.0, 210.0, "A5"));
+    addPaperSize(new DviPaperSize(105.0, 148.0, "A6"));
+    addPaperSize(new DviPaperSize(74.0, 105.0, "A7"));
+    addPaperSize(new DviPaperSize(52.0, 74.0, "A8"));
+    addPaperSize(new DviPaperSize(37.0, 52.0, "A9"));
+    addPaperSize(new DviPaperSize(26.0, 37.0, "A10"));
+  }
+
+  protected void addPaperSize(DviPaperSize dviPaperSize)
+  {
+    if (dviPaperSize == null) return;
+    paperSizes.put(dviPaperSize.description().toLowerCase(), dviPaperSize);
+  }
+
+  public DviPaperSize findPaperSizeByName(String name) throws DviException
+  {
+    if (name == null) return null;
+    return paperSizes.get(name.toLowerCase());
+  }
+
+  public DviPaperSize getDefaultPaperSize() throws DviException
+  {
+    return findPaperSizeByName("A4");
+  }
+
+  public DviPaperSize [] listPaperSizes() throws DviException
+  {
+    return paperSizes.values().toArray(new DviPaperSize[0]);
+  }
+
+  private static final DviResolution defaultResolution = new DviResolution(2400, 20);
+  
+  public DviResolution getDefaultResolution() throws DviException
+  {
+    return defaultResolution;
+  }
+
+// TODO: delete
+//  private static final CachedComputer<String, Collection<URL>> dviResourceComputer
+//    = new CachedComputer<String, Collection<URL>>
+//      (new ThreadedComputer<String, Collection<URL>>(1));
+  
+  public URL getDviResource(String filename) throws DviException
+  {
+    if (filename.startsWith(DVICORE_INTERNAL_FILENAME_PREFIX)) {
+      // The resource name starting with "dvicore-" are only for internal use.
+      // So there is no local file corresponding to such a filename.
+      // We answer null.
+      return null;
+    }
+    
+    Computation<String, Collection<URL>> c
+      = new FileLocationResolver(this, "/dvi/builtin", filename);
+    final Future<Collection<URL>> future
+      = getAsyncComputers().getDviResourceComputer().compute(c);
+    try {
+      final Collection<URL> list = future.get();
+      for (URL url : list) {
+        if (recordResources) {
+          LOGGER.info("resolved resource: filename=" + filename + " url=" + url);
+          resources.add(url);
+        } else {
+          LOGGER.finest("resolved resource: filename=" + filename + " url=" + url);
+        }
+        return url;
+      }
+      return null;
+    } catch (InterruptedException e) {
+      LOGGER.warning(e.toString());
+      throw new DviException(e);
+    } catch (ExecutionException e) {
+      LOGGER.warning(e.toString());
+      throw new DviException(e);
+    }
+  }
+
+  public LogicalFont mapLogicalFont(LogicalFont logicalFont)
+      throws DviException
+  {
+       LogicalFont mapped = logicalFont;
+       if (logicalFont != null) {
+           String prefix = getClass().getName();
+           String faceKey = prefix + ".fontMap." + logicalFont.fontSpec().name();
+        LOGGER.info("Face key: " + faceKey);
+           String face = getProperties().getProperty(faceKey);
+        LOGGER.info("Properties: " + getProperties());
+           if (face != null) {
+                   mapped = logicalFont.renameTo(face);
+               LOGGER.info("Rename logical font: " + logicalFont + " => " + mapped);
+           }
+       }
+    LOGGER.info("Map logical font: " + logicalFont + " => " + mapped);
+    return mapped;
+  }
+  
+  // N.B. System.getProperty() throws an exception when invoked
+  // from inside an applet.
+  private static String getSystemProperty(String key) {
+       try {
+           return System.getProperty(key);
+       } catch (AccessControlException ex) {
+               return null;
+       }
+  }
+
+  private static final String userDir = getSystemProperty("user.dir");
+  private static final String ioTmpDir = getSystemProperty("java.io.tmpdir");
+  
+  // TODO: externalize the string "dvibrowser.jar".
+  public File getApplicationHomeDir()
+  throws DviException
+  {
+       if (userDir != null) {
+           File home = new File(userDir);
+           File markFile = new File(home, "dvibrowser.jar");
+           if (markFile.exists()) {
+             // It seems that we are using DviContext from within dvibrowser.
+             return home;
+           }
+       }
+    return null;
+  }
+
+  public File getCacheDirectory() throws DviException
+  {
+    File appHome = getApplicationHomeDir();
+    if (appHome == null) {
+      File tmpDir = getTemporaryDirectory();
+      if (tmpDir != null) {
+          return new File(tmpDir, "cache");
+      }
+    } else {
+       if (userDir != null) {
+             File var = new File(userDir, "var");
+             return new File(var, "cache");
+       }
+    }
+    return null;
+  }
+
+  public File getTemporaryDirectory() throws DviException
+  {
+    File appHome = getApplicationHomeDir();
+    if (appHome == null) {
+       if (ioTmpDir != null) {
+             return new File(ioTmpDir, "dvicontext");
+       }
+    } else {
+      return new File(appHome, "tmp");
+    }
+    return null;
+  }
+
+  private volatile String [] ghostscriptExecutables = null;
+  
+  public String getExecutableName(String name) throws DviException
+  {
+    if ("gs".equals(name)) {
+      if (ghostscriptExecutables == null) {
+        // The following code might be called in pararell when we invoke
+        // this method from within multiple threads.
+        // It might be better to use some atomic classes.
+        ghostscriptExecutables = GhostscriptUtils.listGhostscriptExecutables();
+        if (ghostscriptExecutables.length == 0) {
+          LOGGER.warning("You don't seem to have a Ghostscript installed.  dvibrowser needs it to render PS, EPS, and PDF.");
+        } else {
+          LOGGER.info("Ghostscript executables found: " + DviUtils.join(" ", ghostscriptExecutables));
+        }
+      }
+      if (ghostscriptExecutables.length > 0) {
+        return ghostscriptExecutables[0];
+      }
+    }
+    
+    return name;
+  }
+
+  private final DviToolkit dviToolkit = new DviToolkit(this);
+  public DviToolkit getDviToolkit()
+  {
+    return dviToolkit;
+  }
+
+  private final MetafontMode libraryDefaultMetafontMode = MetafontMode.FALLBACK;
+  public MetafontMode getDefaultMetafontMode() throws DviException {
+    return libraryDefaultMetafontMode;
+  }
+
+       public void setRecordResources(boolean recordResources) {
+               this.recordResources = recordResources;
+       }
+
+       public boolean wantRecordResources() {
+               return recordResources;
+       }
+
+       public Set<URL> getRecordedResources() {
+               return resources;
+       }
+
+       protected void setAsyncComputers(AsyncComputers asyncComputers) {
+               this.asyncComputers = asyncComputers;
+       }
+
+       public AsyncComputers getAsyncComputers() {
+               return asyncComputers;
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/DviToolkit.java b/src/jp/sourceforge/dvibrowser/dvicore/ctx/DviToolkit.java
new file mode 100644 (file)
index 0000000..fcf79bc
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.ctx;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.awt.image.WritableRaster;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviPaperSize;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.DevicePainter;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.api.Geometer;
+import jp.sourceforge.dvibrowser.dvicore.api.ImageDevice;
+import jp.sourceforge.dvibrowser.dvicore.gui.swing.ViewSpec;
+import jp.sourceforge.dvibrowser.dvicore.image.split.DviImage;
+import jp.sourceforge.dvibrowser.dvicore.image.split.SplitImage;
+import jp.sourceforge.dvibrowser.dvicore.image.split.SplitImageUtils;
+import jp.sourceforge.dvibrowser.dvicore.render.BasicGeometer;
+import jp.sourceforge.dvibrowser.dvicore.render.DviBoundingBoxPreparator;
+import jp.sourceforge.dvibrowser.dvicore.render.DviPagePreparator;
+import jp.sourceforge.dvibrowser.dvicore.render.IntRGBImage;
+import jp.sourceforge.dvibrowser.dvicore.render.RunLengthSampler;
+import jp.sourceforge.dvibrowser.dvicore.special.AnchorSet;
+import jp.sourceforge.dvibrowser.dvicore.special.EPS2ImagePreparator;
+import jp.sourceforge.dvibrowser.dvicore.special.EPS2SplitImagePreparator;
+import jp.sourceforge.dvibrowser.dvicore.special.EmbeddedPostScript;
+import jp.sourceforge.dvibrowser.dvicore.special.EmbeddedPostScriptPreparator;
+import jp.sourceforge.dvibrowser.dvicore.special.HtmlSpecialParser;
+import jp.sourceforge.dvibrowser.dvicore.special.SourceSpecialParser;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.CacheEntry;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.CachedComputer;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.Computation;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.ThreadedComputer;
+
+
+// TODO: Make this an interface
+public class DviToolkit
+extends DviObject
+{
+  private static final Logger LOGGER = Logger.getLogger(DviToolkit.class.getName());
+
+  private static final Map<String, AnchorSet> anchorSetCache = Collections
+      .synchronizedMap(new LinkedHashMap<String, AnchorSet>() {
+        private static final long serialVersionUID = 3603340224879882990L;
+
+        protected boolean removeEldestEntry(Map.Entry<String, AnchorSet> entry) {
+          return size() > 1024;
+        }
+      });
+  
+  public DviToolkit(DviContextSupport dcs)
+  {
+    super(dcs);
+  }
+  
+  public DviRect computeRawBoundingBox(DviPage page, DviResolution res)
+      throws DviException
+  {
+    if (page == null)
+    return null;
+    
+    Computation<String, DviRect> computation
+      = new DviBoundingBoxPreparator(this, page, res);
+    
+    Future<DviRect> future = computerForBoundingBoxPreparator.compute(computation);
+    DviRect bbox;
+    try {
+      bbox = future.get();
+    } catch (InterruptedException e) {
+      throw new DviException(e);
+    } catch (ExecutionException e) {
+      throw new DviException(e);
+    }
+    return bbox;
+  }
+
+  public DviRect computeBoundingBox(DviPage page, DviResolution res)
+      throws DviException
+  {
+    if (page == null)
+      return null;
+    
+    DviRect bbox = computeRawBoundingBox(page, res);
+    if (bbox == null) return bbox;
+    return bbox.shrink(res.shrinkFactor());
+  }
+  
+  public DviRect [] computeRawBoundingBoxes(DviDocument doc, DviResolution res)
+      throws DviException
+  {
+    if (doc == null)
+      return null;
+    
+    List<DviRect> bbox = new ArrayList<DviRect>();
+
+    for (DviPage page : doc.getPages()) {
+      bbox.add(computeRawBoundingBox(page, res));
+    }
+    
+    return bbox.toArray(new DviRect[0]);
+  }
+
+  public DviRect[] computeBoundingBoxes(DviDocument doc, DviResolution res) throws DviException
+  {
+    if (doc == null)
+      return null;
+    
+    DviRect [] raw = computeRawBoundingBoxes(doc, res);
+    DviRect [] ret = new DviRect[raw.length];
+    for (int i=0; i<ret.length; i++) {
+      ret[i] = raw[i].shrink(res.shrinkFactor());
+    }
+    return ret;
+  }
+  
+  private void drawNamedFrame(Graphics gg, String string, Color color, int x, int y, int width, int height)
+  {
+    gg.setColor(color);
+    gg.drawString(string, x, y + 32);
+    gg.drawRect(x, y, width-1, height-1);
+  }
+  
+  private static final boolean renderDebugInfo = false;
+
+  /**
+   * Render the specified page to a BufferedImage.
+   * @param page page to render
+   * @param viewSpec used to specify the view configuration.
+   * @param rect the bounding box in the paper coordinate with resolution specified by viewSpec.
+   * @return BufferedImage INT_RGB image
+   * @throws DviException
+   */
+  public BufferedImage renderToBufferedImage(BufferedImage img, int x, int y, DviPage page, DviRect rect,
+      ViewSpec viewSpec) throws DviException
+  {
+    if (img == null)
+      throw new NullPointerException("img");
+    if (page == null)
+      throw new NullPointerException("page");
+    if (viewSpec == null)
+      throw new NullPointerException("view spec");
+    
+    final DviResolution res = viewSpec.getResolution();
+
+    DviRect paper = computePageBoundingBox(viewSpec.getPaperSize(), res);
+
+    if (rect == null) {
+      rect = paper;
+    }
+
+    // A point (bbox.x + a, bbox.y + b) in the paper coordinate corresponds to
+    // (x + a, y + b) == (paper2dispX + a, paper2dispY + b) in the image coordinate.
+    final int paper2dispX = x - rect.x();
+    final int paper2dispY = y - rect.y();
+
+    WritableRaster raster = img.getRaster();
+    DataBufferInt data = (DataBufferInt) (raster.getDataBuffer());
+
+    IntRGBImage rgbImage = new IntRGBImage(data.getData(), raster.getWidth(),
+        raster.getHeight());
+    rgbImage.fill(viewSpec.getBackgroundColor().toIntRGB());
+
+    if (viewSpec.isPostScriptEnabled()) {
+      try {
+        SplitImage dviImage = getEmbeddedPostScriptAsSplitImage(page, viewSpec);
+        if (dviImage != null) {
+          Graphics2D gg = img.createGraphics();
+          try {
+            gg.setClip(x, y, rect.width(), rect.height());
+            double factor = res.actualDpi() / dviImage.getResolution().actualDpi();
+            DviRect r0 = dviImage.getRect();
+            DviRect r = r0.magnify(factor);
+            DviRect psRect = r
+               .moveTo(paper.bottomLeft().translate(0, - r.bottom()))
+               .translate(paper2dispX, paper2dispY);
+            SplitImageUtils.renderToGraphics(gg, dviImage, psRect.x(), psRect.y(), factor);
+            if (renderDebugInfo) {
+              drawNamedFrame(gg, "PS", Color.black, psRect.x(), psRect.y(), psRect.width(), psRect.height());
+            }
+          } finally {
+            gg.dispose();
+          }
+        }
+      } catch (OutOfMemoryError e) {
+        DviUtils.logStackTrace(LOGGER, Level.SEVERE, e);
+      }
+    }
+    if (renderDebugInfo) {
+      Graphics gg = img.getGraphics();
+      try {
+        int a = (int) res.actualDpi();
+        DviRect inchBox = paper.crop(a, a, a, a);
+        drawNamedFrame(gg, "Paper", Color.blue, paper2dispX + paper.x(),
+            paper2dispY + paper.y(), paper.width(), paper.height());
+        drawNamedFrame(gg, "InchBox", Color.red, paper2dispX + inchBox.x(),
+            paper2dispY + inchBox.y(), inchBox.width(), inchBox.height());
+        DviRect bbox = computeBoundingBox(page, res);
+        drawNamedFrame(gg, "BBOX", Color.cyan, paper2dispX + bbox.x(),
+            paper2dispY + bbox.y(), bbox.width(), bbox.height());
+      } finally {
+        gg.dispose();
+      }
+    }
+
+    ImageDevice out = rgbImage
+        .getImageDevice(res, viewSpec.getGammaCorrector());
+    out.setColor(viewSpec.getForegroundColor());
+    out.translate(paper2dispX, paper2dispY);
+    DevicePainter dp = getDviContext().newDevicePainter();
+    dp.setOutput(new RunLengthSampler(out));
+    Geometer geometer = new BasicGeometer(this);
+    geometer.setPainter(dp);
+    getDviContext().execute(page, geometer);
+
+    return img;
+  }
+  
+  public BufferedImage createCompatibleBufferedImage(int width, int height)
+  {
+    final BufferedImage img = new BufferedImage
+      (width, height, BufferedImage.TYPE_INT_RGB);
+    return img;
+  }
+
+  public BufferedImage renderToBufferedImage(DviPage page, DviRect bbox,
+    ViewSpec viewSpec) throws DviException
+  {
+    final DviResolution res = viewSpec.getResolution();
+
+    DviRect paper = computePageBoundingBox(viewSpec.getPaperSize(), res);
+
+    if (bbox == null) {
+      bbox = paper;
+    }
+    
+    final BufferedImage img = createCompatibleBufferedImage(bbox.width(), bbox.height());
+    return renderToBufferedImage(img, 0, 0, page, bbox, viewSpec);
+  }
+  
+  public void prepareForRendering(DviDocument doc, ViewSpec viewSpec)
+  throws DviException
+  {
+    if (doc == null) return;
+    if (doc.getTotalPages() > 0) {
+      DviPage page = doc.getPage(0);
+      prepareForRendering(page, viewSpec);
+      computeBoundingBoxes(doc, viewSpec.getResolution());
+    }
+  }
+
+  private AnchorSet buildAnchorSetForDocument(DviDocument doc)
+  {
+    AnchorSet as = new AnchorSet();
+    {
+      try {
+        HtmlSpecialParser hse = new HtmlSpecialParser(this);
+        hse.execute(doc);
+        LOGGER.fine("Extracted Html tags from " + doc);
+        as.addAll(hse.getAnchorSet());
+      } catch (DviException e) {
+        LOGGER.warning(e.toString());
+      }
+    }
+    {
+      try {
+        SourceSpecialParser hse = new SourceSpecialParser(this);
+        hse.execute(doc);
+        LOGGER.fine("Extracted Source specials from " + doc);
+        as.addAll(hse.getAnchorSet());
+      } catch (DviException e) {
+        LOGGER.warning(e.toString());
+      }
+    }
+    return as;
+  }
+
+  public AnchorSet getAnchorSet(DviPage page)
+  throws DviException
+  {
+    if (page == null) return null;
+    DviDocument doc = page.getDocument();
+    String key = doc.getCacheKey();
+    AnchorSet as = anchorSetCache.get(key);
+    if (as == null) {
+      as = buildAnchorSetForDocument(doc);
+      if (as != null) {
+        anchorSetCache.put(key, as);
+        LOGGER.fine("Cached AnchorSet with key " + key);
+      }
+    }
+    
+    if (as == null) {
+      return null;
+    }
+
+    AnchorSet pageAnchorSet = new AnchorSet();
+    pageAnchorSet.addAll(as.intersect(page.range()));
+    return pageAnchorSet;
+  }
+
+  public DviRect computePageBoundingBox(DviPaperSize paperSize, DviResolution res)
+  {
+    DviRect bbox = null;
+    try {
+      paperSize = getDviContext().getDefaultPaperSize();
+    } catch (DviException e) {
+      LOGGER.warning(e.toString());
+    }
+    if (paperSize == null) {
+      LOGGER.warning("The default paper size is undefined. We use A4.");
+      paperSize = new DviPaperSize(210.0, 297.0, "A4 internal");
+    }
+
+    bbox = paperSize.toBoundingBox(res);
+    
+    return bbox;
+  }
+
+
+  public void prepareForRendering(DviPage page, ViewSpec viewSpec)
+  {
+    Computation<String, Long> computation = new DviPagePreparator(this, page, viewSpec);
+    try {
+      computerForPreparator.compute(computation).get();
+    } catch (InterruptedException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+    } catch (ExecutionException e) {
+      DviUtils.logStackTrace(LOGGER, Level.SEVERE, e);
+    }
+  }
+
+  public boolean canRenderPageImmediately(DviPage page, ViewSpec viewSpec)
+  {
+    Computation<String, Long> computation = new DviPagePreparator(this, page, viewSpec);
+    Future<Long> future = computerForPreparator.getCachedResult(computation);
+    return future != null && future.isDone();
+  }
+  
+  
+
+  public EmbeddedPostScript getEmbeddedPostScript(DviDocument doc, ViewSpec viewSpec)
+      throws DviException
+  {
+    Computation<String, EmbeddedPostScript> computation
+      = new EmbeddedPostScriptPreparator(this, doc, viewSpec);
+    Future<EmbeddedPostScript> future = computerForEmbeddedPostScriptPreparator.compute(computation);
+    try {
+      return future.get();
+    } catch (InterruptedException e) {
+      throw new DviException(e);
+    } catch (ExecutionException e) {
+      throw new DviException(e);
+    }
+  }
+
+  public String getEmbeddedPostScript(DviPage page, ViewSpec viewSpec)
+  throws DviException
+  {
+    if (page == null) return null;
+    EmbeddedPostScript eps = getEmbeddedPostScript(page.getDocument(), viewSpec);
+    return eps.toPostScript(page.getPageNumber(), viewSpec.getEpsResolutionDpi());
+  }
+
+
+  public DviImage getEmbeddedPostScriptAsImage(DviPage page, ViewSpec viewSpec)
+  throws DviException
+  {
+    Computation<String, DviImage> computation = new EPS2ImagePreparator(this,
+        page, viewSpec);
+    Future<DviImage> future = computerForEPS2Image.compute(computation);
+    try {
+      return future.get();
+    } catch (InterruptedException e) {
+      throw new DviException(e);
+    } catch (ExecutionException e) {
+      throw new DviException(e);
+    }
+  }
+  
+  private static final CachedComputer<String, Long> computerForPreparator = new CachedComputer<String, Long>(
+      new ThreadedComputer<String, Long>(1)) {
+    @Override
+    protected boolean removeEldestEntry(
+        Map.Entry<String, CacheEntry<String, Long>> entry) {
+      return getCache().size() > 1024;
+    }
+  };
+
+  private static final CachedComputer<String, EmbeddedPostScript> computerForEmbeddedPostScriptPreparator = new CachedComputer<String, EmbeddedPostScript>(
+      new ThreadedComputer<String, EmbeddedPostScript>(1)) {
+    @Override
+    protected boolean removeEldestEntry(
+        Map.Entry<String, CacheEntry<String, EmbeddedPostScript>> entry) {
+      return getCache().size() > 1024;
+    }
+  };
+
+  private static final CachedComputer<String, DviImage> computerForEPS2Image = new CachedComputer<String, DviImage>(
+      new ThreadedComputer<String, DviImage>(1)) {
+    @Override
+    protected boolean removeEldestEntry(
+        Map.Entry<String, CacheEntry<String, DviImage>> entry) {
+      return getCache().size() > 10;
+    }
+  };
+
+  private static final CachedComputer<String, SplitImage> computerForEPS2SplitImage = new CachedComputer<String, SplitImage>(
+      new ThreadedComputer<String, SplitImage>(1)) {
+    @Override
+    protected boolean removeEldestEntry(
+        Map.Entry<String, CacheEntry<String, SplitImage>> entry) {
+      return getCache().size() > 10;
+    }
+  };
+  
+  private static final CachedComputer<String, DviRect> computerForBoundingBoxPreparator = new CachedComputer<String, DviRect>(
+      new ThreadedComputer<String, DviRect>(1)) {
+    @Override
+    protected boolean removeEldestEntry(
+        Map.Entry<String, CacheEntry<String, DviRect>> entry) {
+      return getCache().size() > 1024;
+    }
+  };
+  
+  
+  public SplitImage getEmbeddedPostScriptAsSplitImage(DviPage page, ViewSpec viewSpec)
+  throws DviException
+  {
+    Computation<String, SplitImage> computation = new EPS2SplitImagePreparator(this,
+        page, viewSpec);
+    Future<SplitImage> future = computerForEPS2SplitImage.compute(computation);
+    try {
+      return future.get();
+    } catch (InterruptedException e) {
+      throw new DviException(e);
+    } catch (ExecutionException e) {
+      throw new DviException(e);
+    }
+  }
+
+  
+  // This is slow.
+  public BufferedImage getScaledImage(int width, int height, BufferedImage image) {
+    if (image == null) return null;
+    int origWidth = image.getWidth();
+    int origHeight = image.getHeight();
+    if (width < 0) {
+      if (origHeight > 0) {
+        width = origWidth * height / origHeight;
+      } else {
+        width = 0;
+      }
+    } else if (height < 0) {
+      if (origWidth > 0) {
+        height = origHeight * width / origWidth;
+      } else {
+        height = 0;
+      }
+    }
+    BufferedImage out = createCompatibleBufferedImage(width, height);
+    Graphics2D g = out.createGraphics();
+    g.drawImage(image.getScaledInstance(width, height, Image.SCALE_SMOOTH), null, null);
+    g.dispose();
+    return out;
+  }
+
+  
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/FileLocationResolver.java b/src/jp/sourceforge/dvibrowser/dvicore/ctx/FileLocationResolver.java
new file mode 100644 (file)
index 0000000..b64c989
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.ctx;
+import java.io.File;
+import java.net.URL;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.Computation;
+import jp.sourceforge.dvibrowser.dvicore.util.progress.ProgressItem;
+
+
+public class FileLocationResolver
+extends DviObject
+implements Computation<String, Collection<URL>> {
+  private static final Logger LOGGER = Logger.getLogger(FileLocationResolver.class
+      .getName());
+  private final String systemResourcePath;
+  private final String filename;
+  
+  public FileLocationResolver(DviContextSupport dcs, String systemResourcePath, String filename)
+  {
+    super(dcs);
+    this.filename = filename;
+    this.systemResourcePath = systemResourcePath;
+  }
+
+  public Collection<URL> call() throws Exception
+  {
+    ProgressItem progress = getDviContext().getProgressRecorder().open("preparing " + filename);
+    List<URL> list = new ArrayList<URL>();
+    try {
+      try {
+        if ("true".equals(System.getProperty("dvi.ctx.DefaultDviContext.usePackageShareDir"))) {
+          File f = new File("share", filename);
+          if (f.exists() && f.canRead()) {
+            LOGGER.fine("Using resource from share: " + f);
+            list.add(f.toURL());
+            return list;
+          }
+        }
+      } catch (AccessControlException ex) {
+          LOGGER.warning(ex.toString());
+      }
+      
+      LOGGER.finer("running kpsewhich: " + filename);
+      try {
+        URL url = null;
+        KpseWhich kpseWhich = new KpseWhich(this);
+        url = kpseWhich.findURL(filename, true);
+        LOGGER.finer("kpsewhich result: " + filename + " => " + url);
+        if (url != null)
+          list.add(url);
+      } catch (RuntimeException ex) {
+          LOGGER.warning(ex.toString());
+      }
+      
+      try {
+        URL u = ClassLoader.getSystemResource(systemResourcePath + "/"
+            + filename);
+        LOGGER.fine("system resource by classloader: " + filename + " => " + u);
+        if (u != null)
+          list.add(u);
+      } catch (RuntimeException ex) {
+          LOGGER.warning(ex.toString());
+      }
+      
+      try {
+          URL u = getClass().getResource(systemResourcePath + "/"
+              + filename);
+          LOGGER.fine("resource by classloader: " + filename + " => " + u);
+          if (u != null)
+            list.add(u);
+        } catch (RuntimeException ex) {
+            LOGGER.warning(ex.toString());
+        }
+
+      
+      if (list.size() == 0) {
+          LOGGER.warning("Failed to resolve file: " + filename);
+      }
+    } finally {
+      progress.close();
+    }
+    
+    return list;
+  }
+  
+  public String getFilename()
+  {
+    return filename;
+  }
+  
+  public String getCacheKey()
+  {
+    return filename;
+  }
+
+  public String getSystemResourcePath()
+  {
+    return systemResourcePath;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/KpseWhich.java b/src/jp/sourceforge/dvibrowser/dvicore/ctx/KpseWhich.java
new file mode 100644 (file)
index 0000000..f4a4f78
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.ctx;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Vector;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.MetafontMode;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.plat.cygwin.CygwinUtils;
+import jp.sourceforge.dvibrowser.dvicore.util.CommandShell;
+import jp.sourceforge.dvibrowser.dvicore.util.CommandShellHandler;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+public class KpseWhich
+extends DviObject
+{
+  public static final int KPSEWHICH_TYPE_UNKNOWN = -1;
+  public static final int KPSEWHICH_TYPE_DEFAULT = 0;
+  public static final int KPSEWHICH_TYPE_WINDOWS_SLASH = 1;
+  public static final int KPSEWHICH_TYPE_CYGWIN = 2;
+  
+  private static final Logger LOGGER = Logger.getLogger(KpseWhich.class.getName());
+  private static boolean available = false;
+  private static volatile boolean availabilityChecked = false;
+  
+  private static int type = -1;
+  private boolean useMktexCommand = true;
+
+  public KpseWhich(DviContextSupport dcs)
+  {
+    super(dcs);
+    if (!availabilityChecked) {
+      checkAvailability();
+    }
+  }
+  
+  private static void setAvailability(boolean isAvailable)
+  {
+    available = isAvailable;
+    availabilityChecked = true;
+  }
+
+  // TODO: outsource the test filename.
+  private synchronized void checkAvailability() {
+    boolean success = true;
+    try {
+      String filename = "cmr10.mf";
+      String result = findURLInternal(filename, true);
+      if (result == null) {
+        LOGGER.warning("Unable to locate by kpsewhich: " + filename);
+        success = false;
+      } else {
+        LOGGER.info("kpsewhich answers: filename=" + filename + " result=" + result);
+        File f = new File(result);
+        if (!f.exists()) {
+          if (DviUtils.isWindows()) {
+            LOGGER.info("Trying cygpath to map " + result + " to system path.");
+            {
+              File f2 = replaceSlashInWindows(result);
+              if (f2 != null) {
+                if (f2.exists()) {
+                  LOGGER.info("The mapped path seems to work: " + f2.getAbsolutePath());
+                  LOGGER.info("kpsewhich seems to encode path with /.");
+                  type = KPSEWHICH_TYPE_WINDOWS_SLASH;
+                }
+              }
+            }
+            
+            if (type < 0) {
+              File f2 = fromPosixPathCygwin(result);
+              if (f2.exists()) {
+                LOGGER.info("The mapped path seems to work: " + f2.getAbsolutePath());
+                LOGGER.info("kpsewhich seems to use the cygwin style path.");
+                type = KPSEWHICH_TYPE_CYGWIN;
+              }
+            }
+            
+            if (type >= 0) {
+              LOGGER.warning("kpsewhich type resolved: " + type);
+              success = true;
+            } else {
+              LOGGER.warning("Unrecognized output by kpsewhich: " + result);
+              success = false;
+            }
+          } else {
+            LOGGER.warning("Unable to find file: " + result);
+            success = false;
+          }
+        } else {
+          LOGGER.info("Using default kpsewhich.");
+          type = KPSEWHICH_TYPE_DEFAULT;
+          success = true;
+        }
+      }
+    } catch (MalformedURLException ex) {
+      success = false;
+    } catch (DviException ex) {
+      success = false;
+    } catch (RuntimeException ex) {
+      success = false;
+    }
+    setAvailability(success);
+    if (!success) {
+      LOGGER.info("kpsewhich is not available");
+    }
+  }
+  
+  private static final Pattern patSlashInWindows = Pattern.compile("^[A-Za-z]:.*$");
+  private File replaceSlashInWindows(String path) {
+    Matcher mat = patSlashInWindows.matcher(path);
+    if (mat.matches()) {
+      return new File(path.replaceAll("/", "\\"));
+    }
+    return null;
+  }
+
+  private File fromPosixPathCygwin(String posixPath) {
+    try {
+      String windowsPath = CygwinUtils.posixPathToJavaPath(posixPath);
+      return new File(windowsPath);
+    } catch (InterruptedException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      return null;
+    } catch (IOException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      return null;
+    } catch (DviException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      return null;
+    }
+  }
+
+  public URL findURL(String name) throws MalformedURLException, DviException
+  {
+    return findURL(name, false);
+  }
+  
+  private String findURLInternal(String name, boolean mustExist) throws MalformedURLException, DviException
+  {
+    String result = null;
+    try {
+      DviContext ctx = getDviContext();
+      MetafontMode mfmode = getMetafontMode();
+      Vector<String> cmdLine = new Vector<String>();
+      cmdLine.add(ctx.getExecutableName("kpsewhich"));
+      if (mustExist) {
+        cmdLine.add("-must-exist");
+      }
+      
+      if (useMktexThroughKpsewhich()) {
+        cmdLine.add("-mktex=pk");
+        cmdLine.add("-mktex=tex");
+        cmdLine.add("-mktex=mf");
+        cmdLine.add("-mktex=tfm");
+      }
+      if (mfmode != null) {
+        cmdLine.add("-dpi=" + mfmode.getBdpi());
+        cmdLine.add("-mode=" + mfmode.getMode());
+      }
+      cmdLine.add(name);
+      
+      final ArrayList<String> stderrData = new ArrayList<String>();
+      final ArrayList<String> stdoutData = new ArrayList<String>();
+      CommandShell cs = new CommandShell();
+      cs.setCommandLine(cmdLine);
+      cs.setHandler(new CommandShellHandler() {
+        public void handleStderr(InputStream in) throws IOException {
+          DviUtils.addLinesFromStream(stderrData, in, LOGGER, Level.FINE, System.err);
+        }
+        public void handleStdout(InputStream in) throws IOException {
+          DviUtils.addLinesFromStream(stdoutData, in);
+        }
+        public void handleStdin(OutputStream out) throws IOException {
+          out.close();
+        }
+      });
+      int ret = cs.execute();
+      if (ret != 0) {
+        LOGGER.fine("kpsewhich command failed with retcode=" + ret + " cmdline=" +
+            DviUtils.join(" ", cmdLine) + " stderr=" + DviUtils.join("\n", stderrData));
+      } else {
+        if (stdoutData.size() > 0) {
+          result = stdoutData.get(0);
+        }
+      }
+    } catch (Exception e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+    }
+
+    LOGGER.fine("kpsewhich result: name=" + name + " result=" + result);
+    
+    return result;
+  }
+
+
+  protected MetafontMode getMetafontMode()
+  throws DviException
+  {
+    MetafontMode mfmode = getDviContext().getDefaultMetafontMode();
+    if (mfmode == null) {
+      mfmode = MetafontMode.FALLBACK;
+    }
+    return mfmode;
+  }
+
+  public URL findURL(String name, boolean mustExist) throws MalformedURLException, DviException
+  {
+    if (!available) return null;
+    String path = findURLInternal(name, mustExist);
+    
+    if (path == null) return null;
+    
+    // The result kpsewhich returns is a POSIX absolute path to the resource in cygwin environment.
+    // Such a path does not work with the File class.  So we have to convert it by
+    // running the command: cygpath -w <path-output-by-kpsewhich>
+
+    File file = null;
+    switch (type) {
+    case KPSEWHICH_TYPE_DEFAULT:
+      file = new File(path);
+      break;
+    case KPSEWHICH_TYPE_CYGWIN:
+      file = fromPosixPathCygwin(path);
+      break;
+    case KPSEWHICH_TYPE_WINDOWS_SLASH:
+      file = replaceSlashInWindows(path);
+      break;
+    default:
+      throw new InternalError("unknown kpsewhich type: " + type);
+    }
+    
+    if (mustExist) {
+      if (!file.exists()) {
+        file = null;
+      }
+    }
+    
+    if (file != null) {
+      return file.toURL();
+    }
+    
+    return null;
+  }
+
+  public void setUseMktexThroughKpsewhich(boolean useMktexCommand) {
+    this.useMktexCommand = useMktexCommand;
+  }
+
+  public boolean useMktexThroughKpsewhich() {
+    return useMktexCommand;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/SimpleJisToUnicodeMapper.java b/src/jp/sourceforge/dvibrowser/dvicore/ctx/SimpleJisToUnicodeMapper.java
new file mode 100644 (file)
index 0000000..6c7c181
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.ctx;
+
+import java.io.UnsupportedEncodingException;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.CharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.font.LogicalFont;
+
+
+public class SimpleJisToUnicodeMapper implements CharacterCodeMapper {
+  public String mapCharacterCodeToUnicode(LogicalFont logicalFont, int jis)
+      throws DviException
+  {
+    if (jis < 0x2120) {
+      return String.valueOf((char) jis);
+    }
+    
+    try {
+      String unicode = new String(new byte[] {
+          (byte)(((jis >> 8) & 0xff) + 0x80),
+          (byte)((jis & 0xff) + 0x80)
+        }
+        , "EUC-JP"
+      );
+      return unicode;
+    } catch (UnsupportedEncodingException e) {
+      throw new DviException(e);
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/Type1DefaultCharacterCodeMapper.java b/src/jp/sourceforge/dvibrowser/dvicore/ctx/Type1DefaultCharacterCodeMapper.java
new file mode 100644 (file)
index 0000000..828a163
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.ctx;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.CharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.font.LogicalFont;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+import jp.sourceforge.dvibrowser.dvicore.util.csv.CsvCellCodec;
+import jp.sourceforge.dvibrowser.dvicore.util.csv.CsvData;
+import jp.sourceforge.dvibrowser.dvicore.util.csv.CsvException;
+
+
+public class Type1DefaultCharacterCodeMapper implements CharacterCodeMapper {
+  private static final Logger LOGGER = Logger.getLogger(Type1DefaultCharacterCodeMapper.class.getName());
+  
+  public static final String ENC_OT1   = "ot1-enc";
+  public static final String ENC_CMSY  = "cmsy-enc";
+  public static final String ENC_CMMI  = "cmmi-enc";
+  public static final String ENC_CMEX  = "cmex-enc";
+  public static final String ENC_CMTT  = "cmtt-enc";
+  public static final String ENC_T2    = "t2-enc";
+  public static final String ENC_CORK  = "cork-enc";
+  public static final String ENC_TS1   = "ts1-enc";
+  public static final String ENC_YFRAK = "yfrak-enc";
+  
+  private int [] table = null;
+
+  private final String encName;
+  
+  public Type1DefaultCharacterCodeMapper(String encName)
+  {
+    this.encName = encName;
+  }
+  
+  public String mapCharacterCodeToUnicode(LogicalFont logicalFont, int codePoint)
+      throws DviException
+  {
+    try {
+      codePoint = mapCodePoint(codePoint);
+    } catch (IOException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    } catch (CsvException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    }
+    if (Character.charCount(codePoint) == 1) {
+      return String.valueOf((char) codePoint);
+    } else {
+      return new String(Character.toChars(codePoint));
+    }
+  }
+
+  protected int mapCodePoint(int codePoint) throws IOException, CsvException, DviException {
+    initTable();
+    if (codePoint < table.length) {
+      return table[codePoint];
+    }
+    return codePoint;
+  }
+  
+  private synchronized void initTable() throws IOException, CsvException, DviException
+  {
+    if (table == null) {
+      doInitTable();
+    }
+  }
+  
+  private void doInitTable() throws IOException, CsvException, DviException {
+    CsvData<String, Integer> data = new CsvData<String, Integer>(new CsvCellCodec<String, Integer>() {
+      public String decodeKey(String s) {
+        return s;
+      }
+
+      public String encodeKey(String key) {
+        return key;
+      }
+
+      public Integer decodeValue(String key, String s) {
+        return Integer.decode(s);
+      }
+
+      public String encodeValue(String key, Integer value) {
+        return String.format("0x%x", value.intValue());
+      }
+    });
+    URL url = getEncodingCsvFile(getTexEncodingName());
+    data.readFromStream(url.openStream());
+    table = new int [128];
+    for (int i=0; i<data.getRowCount(); i++) {
+      int texchar = data.get(i, "texchar");
+      int pfbchar = data.get(i, "pfbchar");
+      if (0 <= texchar && texchar < table.length) {
+        table[texchar] = pfbchar;
+        LOGGER.fine(String.format("MAP: %02x=>%06x\n", texchar, pfbchar));
+      } else {
+        throw new DviException
+          ("texchar value out of range: " + texchar);
+      }
+    }
+  }
+
+  protected URL getEncodingCsvFile(String texEncodingName) {
+    String filename = texEncodingName + ".csv";
+    URL url = getClass().getResource(filename);
+    return url;
+  }
+
+  public String getTexEncodingName() {
+    return encName;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/UnicodeCharacterCodeMapper.java b/src/jp/sourceforge/dvibrowser/dvicore/ctx/UnicodeCharacterCodeMapper.java
new file mode 100644 (file)
index 0000000..3559bf6
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.ctx;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.CharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.font.LogicalFont;
+
+public class UnicodeCharacterCodeMapper implements CharacterCodeMapper {
+  public String mapCharacterCodeToUnicode(LogicalFont logicalFont, int codePoint)
+      throws DviException
+  {
+    if (Character.charCount(codePoint) == 1) {
+      return String.valueOf((char) codePoint);
+    } else {
+      return new String(Character.toChars(codePoint));
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/cmsy-enc.csv b/src/jp/sourceforge/dvibrowser/dvicore/ctx/cmsy-enc.csv
new file mode 100644 (file)
index 0000000..1794c60
--- /dev/null
@@ -0,0 +1 @@
+texchar,pfbchar\r0x00,0x2212\r0x01,0x00b7\r0x02,0x00d7\r0x03,0x2217\r0x04,0x00f7\r0x05,0x0000\r0x06,0x00b1\r0x07,0x0000\r0x08,0x2295\r0x09,0x0000\r0x0a,0x2297\r0x0b,0x0000\r0x0c,0x0000\r0x0d,0x0000\r0x0e,0x0000\r0x0f,0x2022\r0x10,0x0000\r0x11,0x2261\r0x12,0x2286\r0x13,0x2287\r0x14,0x2264\r0x15,0x2265\r0x16,0x0000\r0x17,0x0000\r0x18,0x223c\r0x19,0x2248\r0x1a,0x2282\r0x1b,0x2283\r0x1c,0x0000\r0x1d,0x0000\r0x1e,0x0000\r0x1f,0x0000\r0x20,0x2190\r0x21,0x2192\r0x22,0x2191\r0x23,0x2193\r0x24,0x2194\r0x25,0x0000\r0x26,0x0000\r0x27,0x0000\r0x28,0x21d0\r0x29,0x21d2\r0x2a,0x21d1\r0x2b,0x21d3\r0x2c,0x21d4\r0x2d,0x0000\r0x2e,0x0000\r0x2f,0x221d\r0x30,0x0000\r0x31,0x221e\r0x32,0x2208\r0x33,0x0000\r0x34,0x0000\r0x35,0x0000\r0x36,0x0000\r0x37,0x0000\r0x38,0x2200\r0x39,0x2203\r0x3a,0x00ac\r0x3b,0x2205\r0x3c,0x0000\r0x3d,0x0000\r0x3e,0x0000\r0x3f,0x22a5\r0x40,0x2135\r0x41,0x0041\r0x42,0x0042\r0x43,0x0043\r0x44,0x0044\r0x45,0x0045\r0x46,0x0046\r0x47,0x0047\r0x48,0x0048\r0x49,0x0049\r0x4a,0x004a\r0x4b,0x004b\r0x4c,0x004c\r0x4d,0x004d\r0x4e,0x004e\r0x4f,0x004f\r0x50,0x0050\r0x51,0x0051\r0x52,0x0052\r0x53,0x0053\r0x54,0x0054\r0x55,0x0055\r0x56,0x0056\r0x57,0x0057\r0x58,0x0058\r0x59,0x0059\r0x5a,0x005a\r0x5b,0x222a\r0x5c,0x2229\r0x5d,0x0000\r0x5e,0x2227\r0x5f,0x2228\r0x60,0x0000\r0x61,0x0000\r0x62,0x0000\r0x63,0x0000\r0x64,0x0000\r0x65,0x0000\r0x66,0x007b\r0x67,0x007d\r0x68,0x0000\r0x69,0x0000\r0x6a,0x007c\r0x6b,0x0000\r0x6c,0x0000\r0x6d,0x0000\r0x6e,0x005c\r0x6f,0x0000\r0x70,0x221a\r0x71,0x0000\r0x72,0x0000\r0x73,0x222b\r0x74,0x0000\r0x75,0x0000\r0x76,0x0000\r0x77,0x0000\r0x78,0x00a7\r0x79,0x2020\r0x7a,0x2021\r0x7b,0x00b6\r0x7c,0x2663\r0x7d,0x2666\r0x7e,0x2665\r0x7f,0x2660
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/default-context.properties b/src/jp/sourceforge/dvibrowser/dvicore/ctx/default-context.properties
new file mode 100644 (file)
index 0000000..a8d82bb
--- /dev/null
@@ -0,0 +1,11 @@
+jp.sourceforge.dvibrowser.dvicore.ctx.DefaultDviContext.fontMap.rml = dvicore-awt-dynamic-pk-font-serif
+jp.sourceforge.dvibrowser.dvicore.ctx.DefaultDviContext.fontMap.ryumin-l = dvicore-awt-dynamic-pk-font-serif
+jp.sourceforge.dvibrowser.dvicore.ctx.DefaultDviContext.fontMap.rmlv = dvicore-awt-dynamic-pk-font-serif
+jp.sourceforge.dvibrowser.dvicore.ctx.DefaultDviContext.fontMap.jis = dvicore-awt-dynamic-pk-font-serif
+jp.sourceforge.dvibrowser.dvicore.ctx.DefaultDviContext.fontMap.jisv = dvicore-awt-dynamic-pk-font-serif
+jp.sourceforge.dvibrowser.dvicore.ctx.DefaultDviContext.fontMap.gtbbb-m = dvicore-awt-dynamic-pk-font-sans-serif
+jp.sourceforge.dvibrowser.dvicore.ctx.DefaultDviContext.fontMap.gbm = dvicore-awt-dynamic-pk-font-sans-serif
+jp.sourceforge.dvibrowser.dvicore.ctx.DefaultDviContext.fontMap.gbmv = dvicore-awt-dynamic-pk-font-sans-serif
+jp.sourceforge.dvibrowser.dvicore.ctx.DefaultDviContext.fontMap.jisg = dvicore-awt-dynamic-pk-font-sans-serif
+jp.sourceforge.dvibrowser.dvicore.ctx.DefaultDviContext.fontMap.jisgv = dvicore-awt-dynamic-pk-font-sans-serif
+jp.sourceforge.dvibrowser.dvicore.special.EPS2ImagePreparator.preservePS = true
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/ctx/ot1-enc.csv b/src/jp/sourceforge/dvibrowser/dvicore/ctx/ot1-enc.csv
new file mode 100644 (file)
index 0000000..aef8f6e
--- /dev/null
@@ -0,0 +1 @@
+texchar,pfbchar\r0x00,0x0393\r0x01,0x0394\r0x02,0x0398\r0x03,0x039b\r0x04,0x039e\r0x05,0x03a0\r0x06,0x03a3\r0x07,0x03a5\r0x08,0x03a6\r0x09,0x03a8\r0x0a,0x03a9\r0x0b,0xfb00\r0x0c,0xfb01\r0x0d,0xfb02\r0x0e,0xfb03\r0x0f,0xfb04\r0x10,0x200d\r0x11,0x0237\r0x12,0x0060\r0x13,0x00b4\r0x14,0x02c7\r0x15,0x02d8\r0x16,0x00af\r0x17,0x02da\r0x18,0x00b8\r0x19,0x00df\r0x1a,0x00e6\r0x1b,0x0153\r0x1c,0x00f8\r0x1d,0x00c6\r0x1e,0x0152\r0x1f,0x00d8\r0x20,0x0020\r0x21,0x0021\r0x22,0x201d\r0x23,0x0023\r0x24,0x0024\r0x25,0x0025\r0x26,0x0026\r0x27,0x2019\r0x28,0x0028\r0x29,0x0029\r0x2a,0x002a\r0x2b,0x002b\r0x2c,0x002c\r0x2d,0x002d\r0x2e,0x002e\r0x2f,0x002f\r0x30,0x0030\r0x31,0x0031\r0x32,0x0032\r0x33,0x0033\r0x34,0x0034\r0x35,0x0035\r0x36,0x0036\r0x37,0x0037\r0x38,0x0038\r0x39,0x0039\r0x3a,0x003a\r0x3b,0x003b\r0x3c,0x00a1\r0x3d,0x003d\r0x3e,0x00bf\r0x3f,0x003f\r0x40,0x0040\r0x41,0x0041\r0x42,0x0042\r0x43,0x0043\r0x44,0x0044\r0x45,0x0045\r0x46,0x0046\r0x47,0x0047\r0x48,0x0048\r0x49,0x0049\r0x4a,0x004a\r0x4b,0x004b\r0x4c,0x004c\r0x4d,0x004d\r0x4e,0x004e\r0x4f,0x004f\r0x50,0x0050\r0x51,0x0051\r0x52,0x0052\r0x53,0x0053\r0x54,0x0054\r0x55,0x0055\r0x56,0x0056\r0x57,0x0057\r0x58,0x0058\r0x59,0x0059\r0x5a,0x005a\r0x5b,0x005b\r0x5c,0x201c\r0x5d,0x005d\r0x5e,0x02c6\r0x5f,0x02d9\r0x60,0x2018\r0x61,0x0061\r0x62,0x0062\r0x63,0x0063\r0x64,0x0064\r0x65,0x0065\r0x66,0x0066\r0x67,0x0067\r0x68,0x0068\r0x69,0x0069\r0x6a,0x006a\r0x6b,0x006b\r0x6c,0x006c\r0x6d,0x006d\r0x6e,0x006e\r0x6f,0x006f\r0x70,0x0070\r0x71,0x0071\r0x72,0x0072\r0x73,0x0073\r0x74,0x0074\r0x75,0x0075\r0x76,0x0076\r0x77,0x0077\r0x78,0x0078\r0x79,0x0079\r0x7a,0x007a\r0x7b,0x2013\r0x7c,0x2014\r0x7d,0x02dd\r0x7e,0x02dc\r0x7f,0x00a8
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/doc/DefaultDviPage.java b/src/jp/sourceforge/dvibrowser/dvicore/doc/DefaultDviPage.java
new file mode 100644 (file)
index 0000000..f7d9723
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.doc;
+
+import jp.sourceforge.dvibrowser.dvicore.DviByteRange;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontTable;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+
+public final class DefaultDviPage
+extends DviObject
+implements DviPage//, java.io.Serializable
+{
+  private final DviDocument doc;
+  private final long bop;
+  private final long eop;
+  private final int pageNum;
+
+  DefaultDviPage(DviDocument doc, int pageNum, long bop, long eop)
+  {
+    super(doc);
+    this.doc = doc;
+    this.pageNum = pageNum;
+    this.bop = bop;
+    this.eop = eop;
+  }
+
+  public DviDocument getDocument() { return doc; }
+  public DviUnit getDviUnit() throws DviException { return doc.getDviUnit(); }
+  public DviFontTable getFontTable() throws DviException { return doc.getFontTable(); }
+  public DviByteRange range() { return new DviByteRange(bop, eop); }
+  public int getPageNumber() { return pageNum; }
+
+  public DviInput getInput()
+  throws DviException
+  {
+    return doc.getInput(bop, eop);
+  }
+  
+  public String toString()
+  {
+    return getClass().getName() + "[pageNum=" + pageNum + ",bop=" + bop + ",eop=" + eop + "]";
+  }
+
+  public long getDataSize() throws DviException
+  {
+    return (eop - bop + 1);
+  }
+
+  public DviInput getInput(long start, long end) throws DviException
+  {
+    return doc.getInput(bop + start, bop + end);
+  }
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/doc/DirectFileDviDocument.java b/src/jp/sourceforge/dvibrowser/dvicore/doc/DirectFileDviDocument.java
new file mode 100644 (file)
index 0000000..b0c1258
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.doc;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+
+import jp.sourceforge.dvibrowser.dvicore.DviConstants;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviFontTable;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.api.HasURL;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviCommand;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+import jp.sourceforge.dvibrowser.dvicore.io.ByteArrayDviData;
+import jp.sourceforge.dvibrowser.dvicore.io.DviRandomAccessFileInput;
+import jp.sourceforge.dvibrowser.dvicore.render.EmptyDviExecutorHandler;
+
+
+// TODO: Optimize this class.  It's really slow for a large file.
+public class DirectFileDviDocument
+extends DviObject
+implements DviDocument, HasURL
+{
+  public static final long MAX_BUFFER_LENGTH = 10000000;
+    /* Buffer size for the data after postamble. 10MB */
+
+  private final File file;
+
+  private DviPreamble   preamble = null;
+  private DviPostamble  postamble = null;
+  private DviPostPost   postPost = null;
+
+  final DviFontTable fontTable = new DviFontTable();
+
+  private ArrayList<DviPage> pages = new ArrayList<DviPage>();
+
+  public DirectFileDviDocument(DviContextSupport dcs, File file)
+  throws DviException
+  {
+    super(dcs);
+    this.file = file;
+    try {
+      RandomAccessFile in = new RandomAccessFile(file, "r");
+      parseRandomAccessFile(in);
+      in.close();
+    } catch (IOException ex) {
+      throw new DviException(ex);
+    }
+  }
+  
+  private void parseRandomAccessFile(RandomAccessFile in)
+  throws IOException, DviException
+  {
+    // Parse the preamble.
+    {
+      int idByte, num, den, mag, k;
+      byte [] comment;
+
+      if (DviCommand.DVI_PRE != in.readUnsignedByte())
+        throw new DviException
+          ("Format error in dvi file: file doesn't start with pre.");
+
+      idByte = in.readUnsignedByte();
+      num = in.readInt();
+      den = in.readInt();
+      mag = in.readInt();
+
+      k = in.readUnsignedByte();
+      comment = new byte[k];
+      in.readFully(comment);
+
+      preamble = new DviPreamble(
+        idByte, DviUnit.getInstance(num, den, mag), comment);
+    }
+
+    // Determine the location of the postamble.
+    final long postPostPointer;
+    {
+      long pos = in.length() - 1;
+      int postamblePointer;
+      int idByte;
+
+      long paddingSize = 0;
+
+      while (true) {
+        if (pos < 0)
+          throw new DviException(
+            "Dvi file ended while looking for the postamble.");
+        in.seek(pos);
+        if (DviConstants.DVI_TRAILER != in.readUnsignedByte()) break;
+        paddingSize++;
+        pos--;
+      }
+      pos -= 5;
+      if (pos < 0)
+        throw new DviException(
+          "Dvi file ended while looking for the postamble.");
+
+      postPostPointer = pos;
+
+      /* pos -> +0 DVI_POST_POST (U1)
+       *        +1 post_offset   (U4)      
+       *        +5 id_byte       (U1)      
+       *        +6 padding       paddingSize copies of ((byte)223).
+       */
+      in.seek(pos);
+
+      if (DviCommand.DVI_POST_POST != in.readUnsignedByte())
+        throw new DviException(
+          "Format error in dvi file: unable to find post_post.");
+
+      postamblePointer = in.readInt();
+      if (postamblePointer < 0 ||
+        (long) postamblePointer > in.length() - 33)
+          throw new DviException(
+            "Format error in dvi file: file size too short.");
+
+      /* TODO: check id_byte */
+      idByte = in.readUnsignedByte();
+
+      postPost = new DviPostPost(postamblePointer, idByte);
+    }
+
+    in.seek(postPost.postamblePointer());
+
+    /* pos -> + 0 DVI_POST        (U1)
+     *        + 1 bp              (S4)
+     *        + 5 num             (S4)
+     *        + 9 den             (S4)
+     *        +13 mag             (S4)
+     *        +17 maxV            (S4)
+     *        +21 maxH            (S4)
+     *        +25 max_stack_depth (U2)
+     *        +27 total_pages     (U2)
+     *   size = 29 bytes.
+     */
+
+    if (DviCommand.DVI_POST != in.readUnsignedByte())
+      throw new DviException(
+        "Format error in dvi file: unable to find post.");
+
+    {
+      int bp;
+      int num, den, mag;
+      int maxV, maxH, maxStackDepth, totalPages;
+
+      bp   = in.readInt();
+      num  = in.readInt();
+      den  = in.readInt();
+      mag  = in.readInt();
+      maxV = in.readInt();
+      maxH = in.readInt();
+      maxStackDepth = in.readUnsignedShort();
+      totalPages    = in.readUnsignedShort();
+
+      postamble = new DviPostamble(
+        bp, DviUnit.getInstance(num, den, mag),
+        maxV, maxH, maxStackDepth, totalPages);
+    }
+
+    // parse font definitions stored right after the postamble.
+
+    {
+      long buflen = postPostPointer - in.getFilePointer();
+      if (0 < buflen) {
+        if (buflen > MAX_BUFFER_LENGTH)
+          throw new DviException
+            ("Too long data after postamble.");
+
+        final byte [] buf = new byte [(int) buflen];
+        in.readFully(buf);
+
+        getDviContext().execute(
+          new ByteArrayDviData(buf),
+          new EmptyDviExecutorHandler() {
+            public void doDefineFont(int fn, DviFontSpec fs) {
+              fontTable.put(fn, fs);
+            }
+          }
+        );
+      }
+    }
+
+    // TODO: handle embedded data.
+
+    {
+      long bop = postamble.firstBackPointer();
+      long eop = postPost.postamblePointer() - 1;
+      int pageNum = postamble.totalPages();
+
+      while (bop != -1 && pageNum > 0) {
+        in.seek(bop);
+
+        if (DviCommand.DVI_BOP != in.readUnsignedByte())
+          throw new DviException(
+            "Format error in dvi file: broken bop link.");
+
+        pageNum--;
+        // REMARK: pageNum==0 corresponds to the first page.
+        pages.add(0, new DefaultDviPage(DirectFileDviDocument.this, pageNum, bop, eop));
+
+        eop = bop - 1;
+        in.seek(bop + 1 + 4 * 10);
+        bop = in.readInt();
+      }
+
+      if (pageNum != 0)
+        throw new DviException(
+          "Format error in dvi file: wrong number of pages.");
+    }
+  }
+  
+  public int getTotalPages() throws DviException {
+    return postamble.totalPages();
+  }
+
+  public DviUnit getDviUnit() {
+    return postamble.dviUnit();
+  }
+  public DviPreamble getPreamble() {
+    return preamble;
+  }
+  public DviPostamble getPostamble() {
+    return postamble;
+  }
+  public DviPostPost getPostPost() {
+    return postPost;
+  }
+  public DviFontTable getFontTable() {
+    return fontTable;
+  }
+
+  public DviInput getInput()
+  throws DviException
+  {
+    try {
+      RandomAccessFile raf = new RandomAccessFile(getFile(), "r");
+      DviRandomAccessFileInput in = new DviRandomAccessFileInput(raf);
+      return in;
+    } catch (FileNotFoundException e) {
+      throw new DviException(e);
+    }
+  }
+
+  public DviInput getInput(long start, long end) throws DviException
+  {
+    try {
+      RandomAccessFile raf = new RandomAccessFile(getFile(), "r");
+      raf.seek(start);
+      DviRandomAccessFileInput in = new DviRandomAccessFileInput(raf);
+      in.setOffset(start);
+      in.setEnd(end);
+      return in;
+    } catch (FileNotFoundException e) {
+      throw new DviException(e);
+    } catch (IOException e) {
+      throw new DviException(e);
+    }
+  }
+
+//  private DviInput getInputNIO(long start, long end)
+//  throws DviException
+//  {
+//    try {
+//      FileInputStream fis = new FileInputStream(file);
+//      FileChannel fc = fis.getChannel();
+//      MappedByteBuffer bb = fc.map(
+//        FileChannel.MapMode.READ_ONLY,
+//        start, end - start + 1
+//      );
+//      DviByteBufferInput in = new DviByteBufferInput(bb);
+//      in.setOffset(start);
+//      return in;
+//    } catch (Throwable ex) {
+//      Logger.trace(ex);
+//      return getInput(start, end);
+//    }
+//  }
+
+  public DviPage getPage(int p)
+  throws DviException
+  {
+    if (p < 0 || getTotalPages() <= p)
+      throw new IllegalArgumentException
+        ("page number out of bounds.");
+
+    return pages.get(p);
+  }
+
+  public DviPage [] getPages()
+  {
+    return pages.toArray(new DviPage [0]);
+  }
+
+  public long getDataSize() throws DviException
+  {
+    return getFile().length();
+  }
+
+  public File getFile()
+  {
+    return file;
+  }
+
+  public URL getURL() throws DviException
+  {
+    try {
+      return file.toURL();
+    } catch (MalformedURLException e) {
+      throw new DviException(e);
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/doc/StreamDviDocument.java b/src/jp/sourceforge/dvibrowser/dvicore/doc/StreamDviDocument.java
new file mode 100644 (file)
index 0000000..1c13b82
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.doc;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviFontTable;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviData;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+import jp.sourceforge.dvibrowser.dvicore.io.DviInputStreamReader;
+import jp.sourceforge.dvibrowser.dvicore.render.EmptyDviExecutorHandler;
+
+
+// TODO: support document cache
+public class StreamDviDocument
+extends DviObject
+implements DviDocument //, java.io.Serializable
+{
+  public static final long MAX_BUFFER_LENGTH = 10000000; /* 10MB */
+
+  private DviPreamble   preamble = null;
+  private DviPostamble  postamble = null;
+  private DviPostPost   postPost = null;
+
+  private byte [] buf = null;
+
+  final DviFontTable fontTable = new DviFontTable();
+
+  private ArrayList<DviPage> pages = new ArrayList<DviPage>();
+
+  public StreamDviDocument(DviContextSupport dcs, InputStream in)
+  throws DviException
+  {
+    super(dcs);
+    try {
+      parseInputStream(in);
+    } catch (IOException ex) {
+      throw new DviException(ex);
+    }
+  }
+
+  private void parseInputStream(InputStream is)
+  throws IOException, DviException
+  {
+    final MyInputStream mis = new MyInputStream(is);
+    final DviInputStreamReader in = new DviInputStreamReader(mis);
+
+    getDviContext().execute(
+      new DviData() {
+        public DviInput getInput()      { return in; }
+        public DviFontTable getFontTable() { throw new UnsupportedOperationException(); }
+        public DviUnit getDviUnit()     { throw new UnsupportedOperationException(); }
+        public long getDataSize() throws DviException
+        {
+          throw new UnsupportedOperationException();
+        }
+        public DviInput getInput(long start, long end) throws DviException
+        {
+          throw new UnsupportedOperationException();
+        }
+      },
+      new EmptyDviExecutorHandler() {
+        private int pageNum=0;
+        private DviExecutorContext ctx;
+        public void begin(DviExecutorContext ctx) {
+          this.ctx = ctx;
+        }
+        public void end() {
+          this.ctx = null;
+        }
+        public void doPre(DviPreamble pre) {
+          preamble = pre;
+        }
+        public void doPost(DviPostamble post) {
+          postamble = post;
+        }
+        public void doPostPost(DviPostPost pp) {
+          postPost = pp;
+          ctx.setTerminate(true);
+        }
+        private long bop;
+        public void doBop(DviBop bop) {
+          this.bop = ctx.getCommandRange().begin();
+        }
+        public void doEop() {
+          long eop = ctx.getCommandRange().begin();
+          DviDocument doc = StreamDviDocument.this;
+          if (doc.getDviContext() instanceof URLDviDocument) {
+            doc = (URLDviDocument) doc.getDviContext();
+          }
+          DviPage page = createPage(pageNum, bop, eop);
+          pages.add(page);
+          pageNum++;
+        }
+        public void doDefineFont(int fn, DviFontSpec fs) {
+          fontTable.put(fn, fs);
+        }
+      }
+    );
+    if (preamble == null)
+      throw new DviException
+        ("no preamble found in the stream.");
+
+    if (postamble == null)
+      throw new DviException
+        ("no postamble found in the stream.");
+
+    if (postPost == null)
+      throw new DviException
+        ("no postPost found in the stream.");
+
+    buf = mis.baos.toByteArray();
+  }
+
+  protected DviPage createPage(int pageNum, long bop, long eop)
+  {
+    return new DefaultDviPage(this, pageNum, bop, eop);
+  }
+
+  private static class MyInputStream
+  extends FilterInputStream
+  {
+    public MyInputStream(InputStream is) { super(is); }
+    private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    public int read()
+    throws IOException
+    {
+      int c = super.read();
+      baos.write(c);
+      return c;
+    }
+
+    public int read(byte [] b, int off, int len)
+    throws IOException
+    {
+      int r = super.read(b, off, len);
+      if (r > 0) {
+        baos.write(b, off, r);
+      }
+      return r;
+    }
+    public void reset()
+    throws IOException
+    {
+      throw new UnsupportedOperationException();
+    }
+    public long skip(long n)
+    throws IOException
+    {
+      throw new UnsupportedOperationException();
+    }
+    public boolean markSupported()
+    {
+      return false;
+    }
+  }
+
+  public int getTotalPages() throws DviException {
+    return postamble.totalPages();
+  }
+
+  public DviUnit getDviUnit() {
+    return postamble.dviUnit();
+  }
+  public DviPreamble getPreamble() {
+    return preamble;
+  }
+  public DviPostamble getPostamble() {
+    return postamble;
+  }
+  public DviPostPost getPostPost() {
+    return postPost;
+  }
+  public DviFontTable getFontTable() {
+    return fontTable;
+  }
+
+  public DviInput getInput()
+  throws DviException
+  {
+    ByteArrayInputStream bais = new ByteArrayInputStream(buf);
+    return new DviInputStreamReader(bais);
+  }
+  
+  public long getDataSize()
+  {
+    return buf.length;
+  }
+
+  public DviInput getInput(long start, long end)
+  throws DviException
+  {
+    DviInputStreamReader in = new DviInputStreamReader(
+      new ByteArrayInputStream(
+        this.buf, (int) start, (int)(end - start) + 1
+      )
+    );
+    in.setOffset(start);
+    return in;
+  }
+
+  public DviPage getPage(int p)
+  throws DviException
+  {
+    if (p < 0 || getTotalPages() <= p)
+      throw new IllegalArgumentException
+        ("page number out of bounds.");
+
+    return pages.get(p);
+  }
+
+  public DviPage [] getPages()
+  {
+    return pages.toArray(new DviPage [pages.size()]);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/doc/URLDviDocument.java b/src/jp/sourceforge/dvibrowser/dvicore/doc/URLDviDocument.java
new file mode 100644 (file)
index 0000000..aac4149
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.doc;
+import java.io.IOException;
+import java.net.URL;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontTable;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.api.HasURL;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+// TODO: make URLDviDocument a subclass of StreamDviDocument
+public class URLDviDocument extends DviObject implements DviDocument, HasURL {
+//  private static final Logger LOGGER = Logger.getLogger(URLDviDocument.class
+//      .getName());
+  
+  private final URL url;
+  private final DviDocument streamDoc;
+  
+  public URLDviDocument(DviContextSupport dcs, URL url)
+  throws DviException
+  {
+    super(dcs);
+    this.url = url;
+    try {
+      this.streamDoc = new StreamDviDocument(this, url.openStream()) {
+        @Override
+        protected DviPage createPage(int pageNum, long bop, long eop) {
+          return new DefaultDviPage(URLDviDocument.this, pageNum, bop, eop);
+        }
+      };
+    } catch (IOException e) {
+      throw new DviException(e);
+    }
+  }
+
+  public DviPage getPage(int p) throws DviException
+  {
+    return streamDoc.getPage(p);
+  }
+
+  public DviPage[] getPages() throws DviException
+  {
+    return streamDoc.getPages();
+  }
+
+  public DviPostPost getPostPost() throws DviException
+  {
+    return streamDoc.getPostPost();
+  }
+
+  public DviPostamble getPostamble() throws DviException
+  {
+    return streamDoc.getPostamble();
+  }
+
+  public DviPreamble getPreamble() throws DviException
+  {
+    return streamDoc.getPreamble();
+  }
+
+  public int getTotalPages() throws DviException
+  {
+    return streamDoc.getTotalPages();
+  }
+
+  public long getDataSize() throws DviException
+  {
+    return streamDoc.getDataSize();
+  }
+
+  public DviUnit getDviUnit() throws DviException
+  {
+    return streamDoc.getDviUnit();
+  }
+
+  public DviFontTable getFontTable() throws DviException
+  {
+    return streamDoc.getFontTable();
+  }
+
+  public DviInput getInput() throws DviException
+  {
+    return streamDoc.getInput();
+  }
+
+  public DviInput getInput(long start, long end) throws DviException
+  {
+    return streamDoc.getInput(start, end);
+  }
+
+  public String getCacheKey()
+  {
+    return getClass().getName() + "--" + DviUtils.md5Hex(url.toExternalForm())
+         + streamDoc.getCacheKey();
+  }
+
+  public URL getURL() throws DviException
+  {
+    return url;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/event/TDefaultEventModel.java b/src/jp/sourceforge/dvibrowser/dvicore/event/TDefaultEventModel.java
new file mode 100644 (file)
index 0000000..e31870f
--- /dev/null
@@ -0,0 +1,58 @@
+package jp.sourceforge.dvibrowser.dvicore.event;
+
+public class TDefaultEventModel
+implements TEventModel
+{
+  private volatile TEventListener listeners = null;
+
+  public void addListener(TEventListener l)
+  {
+    listeners = TEventMulticaster.add(listeners, l);
+  }
+
+  public void addUniqueListener(TEventListener l)
+  {
+    removeListener(l);
+    addListener(l);
+  }
+
+  public String dumpListeners()
+  {
+    if (listeners instanceof TEventMulticaster) {
+      return ((TEventMulticaster) listeners).toString();
+    } else if (listeners == null) {
+      return "null";
+    } else {
+      return listeners.toString();
+    }
+  }
+
+  public void removeListener(TEventListener l)
+  {
+    listeners = TEventMulticaster.remove(listeners, l);
+  }
+
+  public void removeListeners()
+  {
+    listeners = null;
+  }
+
+  public TEventListener getListeners()
+  {
+    return listeners;
+  }
+
+  public void inheritListeners(TEventProcessor ep)
+  {
+    if (ep == null) return;
+    TEventModel em = ep.getEventModel();
+    if (em == null) return;
+    addListener(em.getListeners());
+  }
+
+  public void processEvent(TEvent e)
+  {
+    if (listeners != null)
+      listeners.handleEvent(e);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/event/TEvent.java b/src/jp/sourceforge/dvibrowser/dvicore/event/TEvent.java
new file mode 100644 (file)
index 0000000..5135f12
--- /dev/null
@@ -0,0 +1,18 @@
+package jp.sourceforge.dvibrowser.dvicore.event;
+
+// TODO: use java.awt.Event
+public class TEvent
+extends java.util.EventObject
+{
+       private static final long serialVersionUID = 6716360144019948710L;
+
+       public TEvent(Object source)
+       {
+               super(source);
+       }
+
+       public String toString()
+       {
+               return super.toString();
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/event/TEventListener.java b/src/jp/sourceforge/dvibrowser/dvicore/event/TEventListener.java
new file mode 100644 (file)
index 0000000..c0f7416
--- /dev/null
@@ -0,0 +1,7 @@
+package jp.sourceforge.dvibrowser.dvicore.event;
+
+public interface TEventListener
+//extends java.util.EventListener
+{
+  public void handleEvent(TEvent e);
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/event/TEventModel.java b/src/jp/sourceforge/dvibrowser/dvicore/event/TEventModel.java
new file mode 100644 (file)
index 0000000..387fdd1
--- /dev/null
@@ -0,0 +1,18 @@
+package jp.sourceforge.dvibrowser.dvicore.event;
+
+public interface TEventModel
+{
+  public void addListener(TEventListener l);
+  public void addUniqueListener(TEventListener l);
+
+  public void removeListener(TEventListener l);
+  public void removeListeners();
+
+  public String dumpListeners();
+
+  public TEventListener getListeners();
+
+  public void inheritListeners(TEventProcessor ep);
+
+  public void processEvent(TEvent e);
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/event/TEventMulticaster.java b/src/jp/sourceforge/dvibrowser/dvicore/event/TEventMulticaster.java
new file mode 100644 (file)
index 0000000..1b98a78
--- /dev/null
@@ -0,0 +1,56 @@
+package jp.sourceforge.dvibrowser.dvicore.event;
+
+public class TEventMulticaster
+implements TEventListener
+{
+  protected final TEventListener a, b;
+
+  protected TEventMulticaster(TEventListener a, TEventListener b)
+  {
+    this.a = a;
+    this.b = b;
+  }
+
+  public static TEventListener add(TEventListener a, TEventListener b)
+  {
+    if (a == null) return b;
+    if (b == null) return a;
+    return new TEventMulticaster(a, b);
+  }
+
+  public static TEventListener remove(TEventListener a, TEventListener b)
+  {
+    if (a == null || a == b) {
+      return null;
+    } else if (a instanceof TEventMulticaster) {
+      return ((TEventMulticaster) a).remove(b);
+    } else {
+      return a;
+    }
+  }
+
+  public void handleEvent(TEvent e)
+  {
+    a.handleEvent(e);
+    b.handleEvent(e);
+  }
+
+  public String toString()
+  {
+    if (a == null) return b.toString();
+    if (b == null) return a.toString();
+    return "(" + a.toString() + "," + b.toString() + ")";
+  }
+
+  protected TEventListener remove(TEventListener o)
+  {
+    if (o == a) return b;
+    if (o == b) return a;
+    TEventListener a2 = remove(a, o);
+    TEventListener b2 = remove(b, o);
+    if (a2 == a && b2 == b) {
+      return this;
+    }
+    return add(a2, b2); 
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/event/TEventProcessor.java b/src/jp/sourceforge/dvibrowser/dvicore/event/TEventProcessor.java
new file mode 100644 (file)
index 0000000..d85c045
--- /dev/null
@@ -0,0 +1,6 @@
+package jp.sourceforge.dvibrowser.dvicore.event;
+
+public interface TEventProcessor
+{
+  public TEventModel getEventModel();
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/event/TEventQueue.java b/src/jp/sourceforge/dvibrowser/dvicore/event/TEventQueue.java
new file mode 100644 (file)
index 0000000..dc506e1
--- /dev/null
@@ -0,0 +1,16 @@
+package jp.sourceforge.dvibrowser.dvicore.event;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class TEventQueue
+extends LinkedBlockingQueue<TEvent>
+{
+       private static final long serialVersionUID = -2631304249462595119L;
+
+       public TEvent poll(long timeout)
+       throws InterruptedException
+       {
+               return poll(timeout, TimeUnit.MILLISECONDS);
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/AWTDynamicPkFont.java b/src/jp/sourceforge/dvibrowser/dvicore/font/AWTDynamicPkFont.java
new file mode 100644 (file)
index 0000000..1127ae1
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.awt.image.Raster;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.CharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+public class AWTDynamicPkFont
+extends AbstractDynamicPkFont
+{
+  private static final Logger LOGGER = Logger.getLogger(AWTDynamicPkFont.class.getName());
+       
+  private static final long serialVersionUID = 6218737476095318238L;
+  private final Font font;
+  private CharacterCodeMapper mapper;
+  private boolean renderBoundigBoxes = false;
+
+  public AWTDynamicPkFont(DviContextSupport dcs, Font font)
+  throws DviException
+  {
+    this(dcs, font, null);
+  }
+  public AWTDynamicPkFont(DviContextSupport dcs, Font font, CharacterCodeMapper mapper)
+  throws DviException
+  {
+    super(dcs);
+    if (font == null)
+      throw new NullPointerException
+        ("font is null");
+    this.font = font;
+    this.setCharacterCodeMapper(mapper);
+  }
+  
+  public boolean hasChar(int code) { return font.canDisplay(code); }
+  
+  protected String mapToUnicode(LogicalFont lf, int code) throws DviException
+  {
+    if (getCharacterCodeMapper() != null) {
+      return getCharacterCodeMapper().mapCharacterCodeToUnicode(lf, code);
+    } else {
+      return getDviContext().getCharacterCodeMapper(lf)
+        .mapCharacterCodeToUnicode(lf, code);
+    }
+  }
+
+  protected PkGlyph generatePkGlyph(LogicalFont lf, int code)
+  throws DviException
+  {
+    String unicode = mapToUnicode(lf, code);
+
+    LOGGER.finest("str=(" + unicode + ") code=0x" + Integer.toHexString(code)
+               + " hex=" + DviUtils.hexDump(unicode));
+    
+    Graphics2D g;
+    BufferedImage img;
+
+    // We instantiate an image to get Graphics2D.
+    img = new BufferedImage(
+      1, 1,
+      BufferedImage.TYPE_BYTE_GRAY
+    );
+
+    DviUnit dviUnit = lf.dviUnit();
+    DviFontSpec fs = lf.fontSpec();
+    DviResolution res = lf.resolution();
+    float fontSize = (float) dviUnit.mapToPixelDouble(fs.spaceSize(), res.dpi());
+    Font derivedFont = font.deriveFont(fontSize);
+    
+//    System.out.println(Integer.toHexString(code) + "(" + unicode + "): " + derivedFont);
+    
+    g = img.createGraphics();
+    FontMetrics fm = g.getFontMetrics(derivedFont);
+    int descent = fm.getMaxDescent();
+    int ascent = fm.getMaxAscent();
+    int leading = fm.getLeading();
+    int maxAdvance = fm.getMaxAdvance();
+    int maxAscent = fm.getMaxAscent();
+    int maxDescent = fm.getMaxDescent();
+    
+    Rectangle2D charBounds = fm.getMaxCharBounds(g);
+    Rectangle2D bounds = fm.getStringBounds(unicode, g);
+    Rectangle2D boundsByChars = fm.getStringBounds(unicode.toCharArray(), 0, 1, g);
+    int x = (int) Math.floor(bounds.getMinX());
+    int y = (int) Math.floor(bounds.getMinY());
+    int width = (int) (bounds.getWidth() + 0.5);
+    int height = (int) (bounds.getHeight() + 0.5);
+    //height = maxAscent + maxDescent;
+    height = (int)(charBounds.getHeight() + 0.5);
+    
+    int padding = 600;
+    
+    // padding = Math.max(maxAscent, maxDescent);
+    padding = (int) (charBounds.getHeight() + 0.5);
+    
+    y = - maxAscent;
+    
+    x += -padding - maxAdvance;
+    y += -padding;
+    width += padding * 2 + maxAdvance * 2;
+    height += padding * 2;
+    
+    int bw = width + 1;
+    int bh = height + 1;
+    
+//    System.out.println("codePoint=" + String.format("x0%06x", code));
+//    System.out.println("bounds=" + bounds);
+//    System.out.println("charBounds=" + charBounds);
+//    System.out.println("padding=" + padding);
+//    System.out.println("boundsByChars=" + boundsByChars);
+//    System.out.println("maxAdvance=" + maxAdvance);
+//    System.out.println("ascent=" + ascent + " max=" + fm.getMaxAscent());
+//    System.out.println("descent=" + descent + " max=" + fm.getMaxDescent());
+//    System.out.println("leading=" + leading);
+//    System.out.println("width=" + width);
+//    System.out.println("height=" + height);
+//    System.out.println("(x, y)=(" + x + "," + y + ")");
+    g.dispose();
+    g = null;
+    
+    img = new BufferedImage(
+      bw, bh,
+      BufferedImage.TYPE_BYTE_GRAY
+    );
+    g = img.createGraphics();
+    g.setFont(derivedFont);
+    g.drawString(unicode, -x, -y);
+    if (renderBoundigBoxes()) {
+      g.drawRect(0, 0, width - 1, height - 1);
+      g.drawRect(0, 0, width - 1 , -y - 1);
+      g.drawRect(0, 0, -x - 1, height -1);
+    }
+    g.dispose();
+
+    Raster raster = img.getRaster();
+    DataBufferByte data = (DataBufferByte) raster.getDataBuffer();
+    RunLengthEncodedGlyph rlg = RunLengthEncodedGlyph.readByteGray(
+      data.getData(),
+      bw, bh,
+      -x, -y
+    );
+    PkGlyph glyph = rlg.toPkGlyph();
+//    System.out.println(code);
+//    BinaryDevice out = new dvi.render.DumpBinaryDevice(System.out);
+//    glyph.rasterizeTo(out);
+    return glyph;
+  }
+  public void setRenderBoundigBoxes(boolean renderBoundigBoxes) {
+    this.renderBoundigBoxes = renderBoundigBoxes;
+  }
+  public boolean renderBoundigBoxes() {
+    return renderBoundigBoxes;
+  }
+  public CharacterCodeMapper getCharacterCodeMapper() {
+    return mapper;
+  }
+  public void setCharacterCodeMapper(CharacterCodeMapper mapper) {
+    this.mapper = mapper;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/AWTDynamicPkFontResolver.java b/src/jp/sourceforge/dvibrowser/dvicore/font/AWTDynamicPkFontResolver.java
new file mode 100644 (file)
index 0000000..0bc7c25
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+import java.awt.Font;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+
+
+public class AWTDynamicPkFontResolver
+extends AbstractDviFontResolver
+{
+  private static final Logger LOGGER = Logger
+      .getLogger(AWTDynamicPkFontResolver.class.getName());
+
+  public AWTDynamicPkFontResolver(DviContextSupport dcs, LogicalFont logicalFont) {
+    super(dcs, logicalFont);
+  }
+
+  @Override
+  public Collection<DviFont> call() throws Exception
+  {
+    Font rawFont = mapToRawAWTFont(getLogicalFont());
+    List<DviFont> list = new ArrayList<DviFont>();
+    if (rawFont != null) {
+      DviFont font = new AWTDynamicPkFont(getDviContext(), rawFont);
+      list.add(font);
+    }
+    return list;
+  }
+  
+  protected Font mapToRawAWTFont(LogicalFont logicalFont)
+  {
+    DviFontSpec fs = logicalFont.fontSpec();
+    DviResolution res = logicalFont.resolution();
+    DviUnit dviUnit = logicalFont.dviUnit();
+    String face = mapToRawAWTFontFace(logicalFont);
+    if (face != null) {
+      LOGGER.fine("Using AWT font face " + face + " to render " + logicalFont);
+      final int fontSize = (int) dviUnit.mapToPixelDouble(fs.spaceSize(), res.dpi());
+      if ("dvicore-awt-dynamic-pk-font-serif".equals(face)) {
+        LOGGER.fine("Using serif for" + logicalFont);
+        return new Font("serif", Font.PLAIN, fontSize);
+      } else if ("dvicore-awt-dynamic-pk-font-sans-serif".equals(face)) {
+        LOGGER.fine("Using sans-serif for" + logicalFont);
+        return new Font("sans-serif", Font.PLAIN, fontSize);
+      } else {
+        LOGGER.fine("No AWT font face to render " + logicalFont);
+        return new Font(face, Font.PLAIN, fontSize);
+      }
+    } else {
+      LOGGER.fine("No AWT font face to render " + logicalFont);
+    }
+    return null;
+  }
+
+
+  protected String mapToRawAWTFontFace(LogicalFont logicalFont)
+  {
+    return logicalFont.fontSpec().name();
+  }
+
+  @Override
+  public String getCacheKey()
+  {
+    LogicalFont spec = getLogicalFont();
+    return spec.fontSpec().fontName()
+    + "-" + spec.fontSpec().checkSum()
+    + "-" + spec.fontSpec().spaceSize()
+    + "-" + spec.fontSpec().designSize()
+    + "-" + spec.resolution().dpi()
+    + "-" + spec.dviUnit()
+    ;
+  }
+
+  @Override
+  protected DviFont createInstanceFromStream(InputStream openStream)
+      throws DviException
+  {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  protected String mapToDviResourceName(LogicalFont logicalFont)
+  {
+    throw new UnsupportedOperationException();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/AbstractDviFontResolver.java b/src/jp/sourceforge/dvibrowser/dvicore/font/AbstractDviFontResolver.java
new file mode 100644 (file)
index 0000000..016d0d2
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+import jp.sourceforge.dvibrowser.dvicore.ctx.AbstractDviResourceResolver;
+
+public abstract class AbstractDviFontResolver
+extends AbstractDviResourceResolver<LogicalFont, DviFont>
+{
+  public AbstractDviFontResolver(DviContextSupport dcs, LogicalFont logicalFont) {
+    super(dcs, logicalFont);
+  }
+
+  public LogicalFont getLogicalFont()
+  {
+    return getSpec();
+  }
+
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/AbstractDynamicPkFont.java b/src/jp/sourceforge/dvibrowser/dvicore/font/AbstractDynamicPkFont.java
new file mode 100644 (file)
index 0000000..6c09cd8
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Map;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+import jp.sourceforge.dvibrowser.dvicore.api.Glyph;
+
+
+public abstract class AbstractDynamicPkFont
+extends DviObject
+implements DviFont
+{
+  private static final long serialVersionUID = -7653383398207050528L;
+
+  public AbstractDynamicPkFont(DviContextSupport dcs)
+  throws DviException
+  {
+    super(dcs);
+  }
+
+  // TODO: make the GlyphCache hierarchical so that we can discard
+  // glyphs of the same font simultaneously.
+  public Glyph getGlyph(LogicalFont lf, int code)
+  throws DviException
+  {
+    String key = lf.fontSpec().toString()
+      + "--" + lf.dviUnit().toString() + "--" + lf.resolution().dpi()
+      + "--" + code;
+    
+    Map<String, Glyph> cache = getDviContext().getGlyphCache();
+    Glyph glyph = cache.get(key);
+    if (glyph != null) return glyph;
+
+    //System.out.println("generating glyph: lf=" + lf + " code=" + code);
+    glyph = generatePkGlyph(lf, code);
+    cache.put(key, glyph);
+
+    return glyph;
+  }
+  
+  protected abstract PkGlyph generatePkGlyph(LogicalFont lf, int code)
+    throws DviException;
+
+  private void writeObject(ObjectOutputStream s)
+  throws IOException
+  {
+    s.defaultWriteObject();
+  }
+
+  private void readObject(ObjectInputStream s)
+  throws IOException, ClassNotFoundException
+  {
+    s.defaultReadObject();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/AbstractMetricsResolver.java b/src/jp/sourceforge/dvibrowser/dvicore/font/AbstractMetricsResolver.java
new file mode 100644 (file)
index 0000000..f4a5e47
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.ctx.AbstractDviResourceResolver;
+
+public abstract class AbstractMetricsResolver<T>
+extends AbstractDviResourceResolver<DviFontSpec, T>
+{
+//  private static final Logger LOGGER = Logger
+//  .getLogger(AbstractMetricsResolver.class.getName());
+//
+  public AbstractMetricsResolver(DviContextSupport dcs, DviFontSpec fontSpec) {
+    super(dcs, fontSpec);
+  }
+
+  public DviFontSpec getFontSpec()
+  {
+    return getSpec();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/DviFontResolver.java b/src/jp/sourceforge/dvibrowser/dvicore/font/DviFontResolver.java
new file mode 100644 (file)
index 0000000..f157504
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.CharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+import jp.sourceforge.dvibrowser.dvicore.ctx.BakomaUnicodeCharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.ctx.Type1DefaultCharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.CacheEntry;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.CachedComputer;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.ThreadedComputer;
+import jp.sourceforge.dvibrowser.dvicore.util.progress.ProgressItem;
+
+
+public class DviFontResolver
+extends AbstractDviFontResolver
+{
+  private static final Logger LOGGER = Logger
+      .getLogger(DviFontResolver.class.getName());
+
+  // TODO: set maximum number of fonts to cache.
+  // TODO: outsource the size configurations.
+  private static final CachedComputer<String, Collection<DviFont>> dviFontComputer
+  = new CachedComputer<String, Collection<DviFont>>
+    (new ThreadedComputer<String, Collection<DviFont>>(3)) {
+    @Override
+    protected boolean removeEldestEntry(Map.Entry<String, CacheEntry<String, Collection<DviFont>>> entry)
+    {
+      boolean remove = getCache().size() > 64;
+      return remove;
+    }
+  };
+
+  private boolean trueTypeEnabled = false;
+
+  private boolean type1Enabled = false;
+
+  private boolean openTypeEnabled = false;
+
+  private boolean pkEnabled = true;
+  
+  private static final CharacterCodeMapper defaultTrueTypeMapper = new BakomaUnicodeCharacterCodeMapper();
+  private static final CharacterCodeMapper defaultType1Mapper
+    = new Type1DefaultCharacterCodeMapper(Type1DefaultCharacterCodeMapper.ENC_OT1);
+
+  public DviFontResolver(DviContextSupport dcs, LogicalFont logicalFont) {
+    super(dcs, logicalFont);
+  }
+
+  @Override
+  public Collection<DviFont> call() throws Exception
+  {
+    ProgressItem progress = getDviContext().getProgressRecorder().open(
+        "resolving " + getLogicalFont().fontSpec().name());
+    try {
+      LOGGER.fine("Resolving DVI font " + getLogicalFont());
+      LogicalFont mappedLogicalFont = getDviContext().mapLogicalFont(
+          getLogicalFont());
+      LOGGER.fine("Mapped logical font is " + mappedLogicalFont);
+      List<DviFont> list = new ArrayList<DviFont>();
+      {
+        // Note: We have to use the original logical font to find a virtual font.
+        {
+          Future<Collection<DviFont>> future = dviFontComputer
+            .compute(new VirtualFontResolver(this, getLogicalFont()));
+          list.addAll(future.get());
+        }
+
+        if (list.size() == 0 && openTypeEnabled()) {
+          TrueTypeFontResolver resolver = new TrueTypeFontResolver(this, mappedLogicalFont);
+          resolver.setFileExtension(".otf");
+          Future<Collection<DviFont>> future = dviFontComputer.compute(resolver);
+          Collection<DviFont> fonts = future.get();
+          list.addAll(fonts);
+        }
+        if (list.size() == 0 && type1Enabled) {
+          Future<Collection<DviFont>> future = dviFontComputer
+          .compute(new Type1FontResolver(this, mappedLogicalFont));
+          Collection<DviFont> fonts = future.get();
+          for (DviFont font : fonts) {
+            if (font instanceof AWTDynamicPkFont) {
+              ((AWTDynamicPkFont) font).setCharacterCodeMapper
+                (defaultType1Mapper);
+            }
+            list.add(font);
+          }
+        }
+        if (list.size() == 0 && trueTypeEnabled) {
+          Future<Collection<DviFont>> future = dviFontComputer
+          .compute(new TrueTypeFontResolver(this, mappedLogicalFont));
+          Collection<DviFont> fonts = future.get();
+          for (DviFont font : fonts) {
+            if (font instanceof AWTDynamicPkFont) {
+              ((AWTDynamicPkFont) font).setCharacterCodeMapper
+                (defaultTrueTypeMapper);
+            }
+            list.add(font);
+          }
+        }
+
+        if (list.size() == 0 && pkEnabled() ) {
+          Future<Collection<DviFont>> future = dviFontComputer
+          .compute(new PkFontResolver(this, mappedLogicalFont));
+          list.addAll(future.get());
+        }
+        
+        if (list.size() == 0) {
+          Future<Collection<DviFont>> future = dviFontComputer
+            .compute(new AWTDynamicPkFontResolver(this, mappedLogicalFont));
+          list.addAll(future.get());
+        }
+      }
+      return list;
+    } finally {
+      progress.close();
+    }
+  }
+  
+  @Override
+  public String getCacheKey()
+  {
+    LogicalFont spec = getLogicalFont();
+    return spec.fontSpec().fontName()
+    + "-" + spec.fontSpec().checkSum()
+    + "-" + spec.fontSpec().spaceSize()
+    + "-" + spec.fontSpec().designSize()
+    + "-" + spec.resolution().dpi()
+    + "-" + spec.dviUnit()
+    ;
+  }
+
+  @Override
+  protected DviFont createInstanceFromStream(InputStream openStream)
+      throws DviException
+  {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  protected String mapToDviResourceName(LogicalFont logicalFont)
+  {
+    throw new UnsupportedOperationException();
+  }
+
+  public void setOpenTypeEnabled(boolean openTypeEnabled) {
+    this.openTypeEnabled = openTypeEnabled;
+  }
+
+  public boolean openTypeEnabled() {
+    return openTypeEnabled;
+  }
+
+  public void setPkEnabled(boolean pkEnabled) {
+    this.pkEnabled = pkEnabled;
+  }
+
+  public boolean pkEnabled() {
+    return pkEnabled;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/FullMetricsResolver.java b/src/jp/sourceforge/dvibrowser/dvicore/font/FullMetricsResolver.java
new file mode 100644 (file)
index 0000000..21674ae
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+import java.io.InputStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.FullMetrics;
+
+
+public class FullMetricsResolver extends AbstractMetricsResolver<FullMetrics> {
+//  private static final Logger LOGGER = Logger
+//  .getLogger(FullMetricsResolver.class.getName());
+
+  public FullMetricsResolver(DviContextSupport dcs, DviFontSpec fontSpec) {
+    super(dcs, fontSpec);
+  }
+
+  @Override
+  protected FullMetrics createInstanceFromStream(InputStream openStream)
+      throws DviException
+  {
+    return new TexFontMetrics(this, openStream);
+  }
+
+  @Override
+  protected String mapToDviResourceName(DviFontSpec spec)
+  {
+    return TexFontMetrics.getTfmFilename(spec);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/LogicalFont.java b/src/jp/sourceforge/dvibrowser/dvicore/font/LogicalFont.java
new file mode 100644 (file)
index 0000000..d32548a
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.util.Canonicalizer;
+import jp.sourceforge.dvibrowser.dvicore.util.SimpleCanonicalizer;
+
+// immutable
+
+public final class LogicalFont
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = -8543173850765210687L;
+  private final DviFontSpec fs;
+  private final DviUnit dviUnit;
+  private final DviResolution res;
+
+  private LogicalFont(DviFontSpec fs, DviUnit dviUnit, DviResolution res) {
+    this.fs = fs;
+    this.dviUnit = dviUnit;
+    this.res = res;
+    if (fs == null) throw new IllegalArgumentException("font spec cannot be null");
+    if (dviUnit == null) throw new IllegalArgumentException("DVI unit cannot be null");
+    if (res == null) throw new IllegalArgumentException("resolution cannot be null");
+  }
+
+  private static final Canonicalizer<LogicalFont> canonicalizer
+    = new SimpleCanonicalizer<LogicalFont>();
+
+  public static LogicalFont getInstance(
+    DviFontSpec fs, DviUnit dviUnit, DviResolution res)
+  {
+    return canonicalizer.canonicalize(
+      new LogicalFont(fs, dviUnit, res)
+    );
+  }
+
+  public DviFontSpec fontSpec()     { return fs; }
+  public DviUnit dviUnit()       { return dviUnit; }
+  public DviResolution resolution() { return res; }
+
+  private String string = null;
+  public String toString() {
+    if (string == null) {
+      string = getClass().getName()
+             + "[" + fs + "," + dviUnit + "," + res + "]"
+             ;
+    }
+    return string;
+  }
+  
+  public LogicalFont renameTo(String newName)
+  {
+    return getInstance(fs.rename(newName), dviUnit, res);
+  }
+
+  public boolean equals(Object obj) {
+    if (obj == this) return true;
+    if (obj instanceof LogicalFont) {
+      LogicalFont lf = (LogicalFont) obj;
+      return fs.equals(lf.fs) &&
+             dviUnit.equals(lf.dviUnit) &&
+             res.equals(lf.res);
+    }
+    return false;
+  }
+
+  public int hashCode() {
+    return fs.hashCode() + 33*(dviUnit.hashCode() + 33*res.hashCode());
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/LogicalGlyph.java b/src/jp/sourceforge/dvibrowser/dvicore/font/LogicalGlyph.java
new file mode 100644 (file)
index 0000000..8e7538e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+// immutable
+
+public class LogicalGlyph
+implements java.io.Serializable
+{
+  private static final long serialVersionUID = -4973535722455103574L;
+  private final LogicalFont lf;
+  private final int code;
+  public LogicalGlyph(LogicalFont lf, int code)
+  {
+    this.lf = lf;
+    this.code = code;
+  }
+
+  public LogicalFont logicalFont() { return lf; }
+  public int code() { return code; }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof LogicalGlyph) {
+      LogicalGlyph a = (LogicalGlyph) obj;
+      return a.lf.equals(lf) && a.code == code;
+    }
+    return false;
+  }
+
+  public int hashCode() {
+    return code + 33*lf.hashCode();
+  }
+
+  public String toString() {
+    return getClass().getName()
+      + "[lf=" + lf
+      + " code=" + code;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/PackedGlyphRasterizer.java b/src/jp/sourceforge/dvibrowser/dvicore/font/PackedGlyphRasterizer.java
new file mode 100644 (file)
index 0000000..9a00e64
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+
+// not thread-safe.
+
+public final class PackedGlyphRasterizer
+{
+  private PkGlyph glyph;
+  private byte [] buf;
+  private int dynF, dynG, dynH;
+  private boolean highNyb;
+  private int offset;
+
+  public void begin(PkGlyph glyph)
+  {
+    this.glyph = glyph;
+    buf = glyph.raster();
+    dynF = glyph.dynF();
+    dynG = ((13-dynF) << 4) + dynF + 1;
+    dynH = ((dynF+1)<<4) - dynF - 1; // 15 * (dynF + 1)
+    offset = 0;
+    highNyb = true;
+  }
+
+  public void end()
+  {
+    glyph = null;
+    buf = null;
+  }
+
+  private int getNybble()
+  throws DviException
+  {
+    if (highNyb) {
+      highNyb = false;
+      return ((buf[offset] >> 4) & 15);
+    } else {
+      highNyb = true;
+      return (buf[offset++] & 15);
+    }
+  }
+
+  private int repeat = 0;
+
+  private int getPackedNumInternal(boolean recursed)
+  throws DviException
+  {
+    int i = getNybble();
+    if (i == 0) {
+      int j;
+      do {
+        j = getNybble();
+        i++;
+      } while (j == 0);
+      while (i-- > 0) {
+        j = (j << 4) | getNybble();
+      }
+      return j - 16 + dynG;
+    } else {
+      if (i <= dynF) {
+        return i;
+      } else if (i < 14) { // dynF < i < 14
+        return (i << 4) + getNybble() - dynH;
+      } else {
+        if (recursed)
+          throw new IllegalStateException
+            ("too many levels of recursion");
+        if (i == 14) {
+          repeat = getPackedNumInternal(true);
+        } else // i == 15
+          repeat = 1;
+        return getPackedNumInternal(true);
+      }
+    }
+  }
+
+  public int getPackedNum()
+  throws DviException
+  {
+    return getPackedNumInternal(false);
+  }
+
+  public void rasterizeTo(final BinaryDevice out)
+  throws DviException 
+  {
+    boolean paintFlag = glyph.turnOn();
+    int w = glyph.width();
+    int h = glyph.height();
+    if (w <= 0 || h <= 0)
+      return;
+    if (out.beginRaster(w, h)) {
+      out.beginLine();
+      while (h > 0) {
+        int count = getPackedNum();
+        while (count > 0) {
+          if (count < w) {
+            out.putBits(count, paintFlag);
+            w -= count;
+            break;
+          } else {
+            out.putBits(w, paintFlag);
+            out.endLine(repeat);
+            count -= w;
+            h -= repeat + 1;
+            w = glyph.width();
+            repeat = 0;
+            out.beginLine();
+          }
+        }
+        paintFlag = !paintFlag;
+      }
+    }
+    out.endRaster();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/PackedSequence.java b/src/jp/sourceforge/dvibrowser/dvicore/font/PackedSequence.java
new file mode 100644 (file)
index 0000000..d1f3456
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+// immutable.
+
+public final class PackedSequence
+implements java.io.Serializable, Cloneable
+{
+  private static final long serialVersionUID = -2179435976613430392L;
+  private byte [] data;
+  private final int dynF;
+  private final int nybLength;
+
+  public PackedSequence(byte [] data, int dynF, int nybLength) {
+    this.data = data;
+    this.dynF = dynF;
+    this.nybLength = nybLength;
+  }
+
+  public byte [] data() { return data.clone(); }
+  public int dynF() { return dynF; }
+  public int nybLength() { return nybLength; }
+
+  public Object clone()
+  {
+    try {
+      PackedSequence ps = (PackedSequence) super.clone();
+      ps.data = data.clone();
+      return ps;
+    } catch (CloneNotSupportedException ex) {
+      throw new InternalError();
+    }
+  }
+
+  public String toString()
+  {
+    String s = getClass().getName()
+             + "[dynF=" + dynF
+             + " nybLength=" + nybLength
+             + " data="
+             ;
+    boolean needComma = false;
+    for (byte a : data) {
+      if (needComma) s += ",";
+      s += String.valueOf(a);
+      needComma = true;
+    }
+
+    return s + "]";
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/PkConstants.java b/src/jp/sourceforge/dvibrowser/dvicore/font/PkConstants.java
new file mode 100644 (file)
index 0000000..a254fb2
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+public final class PkConstants {
+  private PkConstants() {} // disable instantiation
+
+  public static final int PK_ID_BYTE = 89;
+
+  public static final int PK_DYNF_RASTER_BY_BITS = 14;
+  
+  public static final int PK_XXX1 = 240;
+  public static final int PK_XXX2 = 241;
+  public static final int PK_XXX3 = 242;
+  public static final int PK_XXX4 = 243;
+  public static final int PK_YYY  = 244;
+  public static final int PK_POST = 245;
+  public static final int PK_NOP  = 246;
+  public static final int PK_PRE  = 247;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/PkFont.java b/src/jp/sourceforge/dvibrowser/dvicore/font/PkFont.java
new file mode 100644 (file)
index 0000000..093495c
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+import jp.sourceforge.dvibrowser.dvicore.api.Glyph;
+import jp.sourceforge.dvibrowser.dvicore.api.SimpleMetrics;
+import jp.sourceforge.dvibrowser.dvicore.io.DviInputStreamReader;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+public class PkFont
+extends DviObject
+implements DviFont, SimpleMetrics
+{
+  private static final long serialVersionUID = 1531160169076910099L;
+
+  private final File file;
+
+  private int idByte;
+  private byte[] comment;
+
+  private int designSize;
+  private int checkSum;
+  private int hppp;
+  private int vppp;
+  private long bodyOffset;
+  private int numChars;
+
+  private HashMap<Integer, Glyph> glyphs
+      = new HashMap<Integer, Glyph>();
+
+  public PkFont(DviContextSupport dcs, File file) 
+  throws DviException
+  {
+    super(dcs);
+    this.file = file;
+    try {
+      FileInputStream fis = null;
+      try {
+        fis = new FileInputStream(file);
+        parseInputStream(fis);
+      } finally {
+        DviUtils.silentClose(fis);
+      }
+    } catch (DviException ex) {
+      throw ex;
+    } catch (IOException ex) {
+      throw new DviException(ex);
+    }
+  }
+
+  public PkFont(DviContextSupport dcs, InputStream is) 
+  throws DviException
+  {
+    super(dcs);
+    try {
+      parseInputStream(is);
+    } catch (DviException ex) {
+      throw ex;
+    } catch (IOException ex) {
+      throw new DviException(ex);
+    }
+    this.file = null;
+  }
+
+  public boolean hasChar(int code)
+  {
+    return glyphs.containsKey(code);
+  }
+
+  public Glyph getGlyph(LogicalFont lf, int code)
+  {
+    return glyphs.get(code);
+  }
+
+  public int getWidth(int code)
+  {
+    PkGlyph g = (PkGlyph) glyphs.get(code);
+    if (g == null) return 0;
+    return g.width();
+  }
+
+  public int getHeight(int code)
+  {
+    PkGlyph g = (PkGlyph) glyphs.get(code);
+    if (g == null) return 0;
+    return g.height();
+  }
+
+  public int getTfmWidth(int code)
+  {
+    PkGlyph g = (PkGlyph) glyphs.get(code);
+    if (g == null) return 0;
+    return g.getTfmWidth();
+  }
+  
+  public int getIdByte() {
+         return idByte;
+  }
+
+  public byte[] getComment() {
+         return comment.clone();
+  }
+
+  public int getDesignSize() {
+         return designSize;
+  }
+
+  public int getCheckSum() {
+         return checkSum;
+  }
+
+  // TODO: rename this method
+  public int getHppp() {
+         return hppp;
+  }
+
+  // TODO: rename this method
+  public int getVppp() {
+         return vppp;
+  }
+
+  public long getBodyOffset() {
+         return bodyOffset;
+  }
+
+  public int getNumChars() {
+         return numChars;
+  }
+
+  public void parseInputStream(InputStream is)
+  throws IOException, DviException
+  {
+    DviInputStreamReader in = new DviInputStreamReader(
+      new BufferedInputStream(is, 8192)
+    );
+
+    if (in.readU1() != PkConstants.PK_PRE)
+      throw new DviException
+        ("PK file doesn't start with PRE.");
+
+    idByte     = in.readU1();
+    if (idByte != PkConstants.PK_ID_BYTE)
+      throw new DviException
+        ("Unrecognized PK file: wrong idByte: " + idByte);
+    int commentLen = in.readU1();
+    comment = new byte[commentLen];
+    in.readFully(comment);
+    designSize = in.readS4();
+    checkSum   = in.readS4();
+    hppp       = in.readS4();
+    vppp       = in.readS4();
+    bodyOffset = in.getOffset();
+
+    numChars = 0;
+
+    boolean stop = false;
+    while (!stop) {
+      byte [] buf;
+      int c = in.readU1();
+      switch (c) {
+        case PkConstants.PK_XXX4:
+        case PkConstants.PK_XXX3:
+        case PkConstants.PK_XXX2:
+        case PkConstants.PK_XXX1: {
+          int len;
+          len = in.readU(c - PkConstants.PK_XXX1 + 1);
+          buf = new byte[len];
+          in.readFully(buf);
+          // System.out.println("xxx: \"" + new String(buf) + "\"");
+          break;
+        }
+        case PkConstants.PK_YYY: {
+//          int yyy = in.readS4();
+               in.readS4();
+          // System.out.println("yyy: " + yyy);
+          break;
+        }
+        case PkConstants.PK_NOP:
+          break;
+        case PkConstants.PK_POST:
+          stop = true;
+          break;
+        case PkConstants.PK_PRE:
+          throw new DviException
+            ("broken PK file: PRE found in the body of PK file: "
+              + file.getPath());
+        default:
+        {
+//          long pos = in.getOffset() - 1;
+          PkGlyph g = (PkGlyph) PkGlyph.readFromInput(c, in);
+          glyphs.put(g.code(), g);
+        }
+      }
+    }
+  }
+  
+  public static String getDviResourceName(LogicalFont lf)
+  {
+    DviFontSpec fs = lf.fontSpec();
+    DviResolution res = lf.resolution();
+    DviUnit dviUnit = lf.dviUnit();
+    int useDpi = (int) (
+      (double) res.dpi() * fs.spaceSize() * dviUnit.magnification()
+        / fs.designSize() / 1000
+    );
+    if (useDpi <= 0) return null;
+    String pk = fs.name() + "." + useDpi + "pk";
+    return pk;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/PkFontResolver.java b/src/jp/sourceforge/dvibrowser/dvicore/font/PkFontResolver.java
new file mode 100644 (file)
index 0000000..c6b8c23
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+import java.io.InputStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+
+
+public class PkFontResolver
+extends AbstractDviFontResolver
+{
+//  private static final Logger LOGGER = Logger.getLogger(DviFontResolver.class
+//      .getName());
+  public PkFontResolver(DviContextSupport dcs, LogicalFont logicalFont)
+  {
+    super(dcs, logicalFont);
+  }
+
+  @Override
+  protected DviFont createInstanceFromStream(InputStream openStream) throws DviException
+  {
+    return new PkFont(this, openStream);
+  }
+
+  @Override
+  protected String mapToDviResourceName(LogicalFont logicalFont)
+  {
+    return PkFont.getDviResourceName(logicalFont);
+  }
+
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/PkGlyph.java b/src/jp/sourceforge/dvibrowser/dvicore/font/PkGlyph.java
new file mode 100644 (file)
index 0000000..f312474
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+import java.io.IOException;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+import jp.sourceforge.dvibrowser.dvicore.api.Glyph;
+
+
+// immutable.
+
+public class PkGlyph
+implements Glyph
+{
+  private static final long serialVersionUID = 3825743583018783073L;
+
+  public static final PkGlyph EMPTY
+    = new PkGlyph(
+        0, 0,
+        null,
+        0, false,
+        0, 0 
+      );
+
+  private int flagByte;
+  private int dynF;
+  private boolean turnOn;
+  private int packetLength;
+  private int code;
+  private int tfmw;
+  private byte [] raster = null;
+
+  private int xAdvance;
+  private int yAdvance;
+  private int width;
+  private int height;
+  private int xOffset;
+  private int yOffset;
+  private DviRect bbox;
+
+  // This constructor is used internally.
+  private PkGlyph() {}
+
+  public PkGlyph(
+    int width, int height,
+    byte [] raster,
+    int dynF, boolean turnOn,
+    int xOffset, int yOffset)
+  {
+    this.width = width;
+    this.height = height;
+    this.raster = raster;
+    this.dynF = dynF;
+    this.turnOn = turnOn;
+    this.xOffset = xOffset;
+    this.yOffset = yOffset;
+
+    bbox = new DviRect(-xOffset, -yOffset, width, height);
+      
+    flagByte = 7;
+    packetLength = 0;
+    code = 0;
+    tfmw = 0; // TODO: write tfmw.
+    xAdvance = yAdvance = 0;
+  }
+
+  public static Glyph readFromInput(DviInput in)
+  throws DviException, IOException
+  {
+    return readFromInput(in.readU1(), in);
+  }
+
+  public static Glyph readFromInput(int firstByte, DviInput in)
+  throws DviException, IOException
+  {
+    PkGlyph g = new PkGlyph();
+
+    // firstByte =
+    //               7   6   5   4    3     2   1   0
+    //             +---+---+---+---+------+---+---+---+
+    //         MSB |      dynF     |turnOn| flagByte  | LSB.
+    //             +---+---+---+---+------+---+---+---+
+
+    g.flagByte = firstByte & 7;
+    g.turnOn   = (0 != (firstByte & 8));
+    g.dynF     = (firstByte >> 4) & 15;
+
+    int rasterSize;
+    if (g.flagByte == 7) {
+      g.packetLength = in.readS4();
+      g.code         = in.readS4();
+      g.tfmw         = in.readS4();
+      g.xAdvance     = in.readS4();
+      g.yAdvance     = in.readS4();
+      g.width        = in.readS4();
+      g.height       = in.readS4();
+      g.xOffset      = in.readS4();
+      g.yOffset      = in.readS4();
+
+      g.packetLength += 9;
+      rasterSize = g.packetLength - 37;
+    } else if (g.flagByte > 3) {
+      g.packetLength = (g.flagByte - 4) * 65536
+                     + in.readU2();
+      g.code         = in.readU1();
+      g.tfmw         = in.readS3();
+      g.xAdvance     = in.readU2() * 65536;
+      g.yAdvance     = 0;
+      g.width        = in.readU2();
+      g.height       = in.readU2();
+      g.xOffset      = in.readS2();
+      g.yOffset      = in.readS2();
+
+      g.packetLength += 4;
+      rasterSize = g.packetLength - 17;
+    } else {
+      g.packetLength = g.flagByte * 256
+                   + in.readU1();
+      g.code         = in.readU1();
+      g.tfmw         = in.readS3();
+      g.xAdvance     = in.readU1() * 65536;
+      g.yAdvance     = 0;
+      g.width        = in.readU1();
+      g.height       = in.readU1();
+      g.xOffset      = in.readS1();
+      g.yOffset      = in.readS1();
+
+      g.packetLength += 3;
+      rasterSize = g.packetLength - 11;
+    }
+
+    if (rasterSize > 0) {
+      g.raster = new byte[rasterSize];
+      in.readFully(g.raster);
+    } else if (rasterSize == 0) {
+      // TODO: handle the case where rasterSize == 0 && turnOn==true.
+      // This can happen.  It's not an error.
+    } else {
+      throw new DviException
+          ("Negative rasterSize: " + rasterSize);
+    }
+
+    g.bbox = new DviRect(-g.xOffset, -g.yOffset, g.width, g.height);
+
+    return g;
+  }
+
+  public boolean rasterByBits() { return (dynF == PkConstants.PK_DYNF_RASTER_BY_BITS); }
+  public int flagByte()         { return flagByte; }
+  public int dynF()             { return dynF; }
+  public boolean turnOn()       { return turnOn; }
+  public int packetLength()     { return packetLength; }
+
+  public byte [] raster()       { return raster; }
+
+  public int code()             { return code; }
+
+  public int getTfmWidth()      { return tfmw; }
+
+  public int xAdvance()         { return xAdvance; }
+  public int yAdvance()         { return yAdvance; }
+  public int width()            { return width; }
+  public int height()           { return height; }
+  public int xOffset()          { return xOffset; }
+  public int yOffset()          { return yOffset; }
+  public DviRect bounds()    { return bbox; }
+
+  public void rasterizeTo(BinaryDevice out)
+  throws DviException
+  {
+    out.save();
+    try {
+      out.translate(-xOffset, -yOffset);
+      if (rasterByBits()) {
+        // TODO: test this code.
+        RunLengthEncodedGlyph rlg = RunLengthEncodedGlyph.readRasterByBits(
+          raster,
+          width, height,
+          0, 0 // Offsets are not used when drawing a raster by bits.
+        );
+        rlg.rasterizeTo(out);
+      } else {
+        PackedGlyphRasterizer sub = new PackedGlyphRasterizer();
+        sub.begin(this);
+        sub.rasterizeTo(out);
+        sub.end();
+      }
+    } finally {
+      out.restore();
+    }
+  }
+
+  public String toString()
+  {
+    return  getClass().getName()
+            + "[packetLength=" + packetLength
+            + ",flagByte=" + flagByte
+            + ",dynF=" + dynF
+            + ",code=" + code
+            + ",tfmw=" + tfmw
+            + ",xAdvance=" + xAdvance
+            + ",yAdvance=" + yAdvance
+            + ",width=" + width
+            + ",height=" + height
+            + ",xOffset=" + xOffset
+            + ",yOffset=" + yOffset
+            + (
+                (raster != null) ?
+                (",rasterSize=" + raster.length) :
+                ",raster=null"
+              )
+            + "]"
+            ;
+
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/RunLengthEncodedGlyph.java b/src/jp/sourceforge/dvibrowser/dvicore/font/RunLengthEncodedGlyph.java
new file mode 100644 (file)
index 0000000..1e4a57e
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviPoint;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+import jp.sourceforge.dvibrowser.dvicore.render.AbstractDevice;
+
+
+// mutable.
+
+public final class RunLengthEncodedGlyph
+{
+  private static final int DEFAULT_ARRAY_SIZE = 256;
+
+  private int width;
+  private int height;
+  private int xOffset;
+  private int yOffset;
+  private ArrayList<RunLengthEncodedLine> lines;
+
+  public RunLengthEncodedGlyph()
+  {
+    this(0, 0, 0, 0);
+  }
+
+  private RunLengthEncodedGlyph(int width, int height, int xOffset, int yOffset)
+  {
+    this.width = width;
+    this.height = height;
+    this.xOffset = xOffset;
+    this.yOffset = yOffset;
+    this.lines = new ArrayList<RunLengthEncodedLine>(DEFAULT_ARRAY_SIZE);
+  }
+
+  public static RunLengthEncodedGlyph readByteBinary(
+    byte [] buf, int width, int height,
+    int xOffset, int yOffset)
+  {
+    RunLengthEncodedGlyph rlg = new RunLengthEncodedGlyph(width, height, xOffset, yOffset);
+
+    RunLengthEncodedLine prev = null;
+    int pitch = ((width + 7) >>> 3) << 3;
+    int bitOffset = 0;
+    for (int i=0; i<height; i++) {
+      RunLengthEncodedLine line = new RunLengthEncodedLine();
+      line.append(buf, bitOffset, width);
+      if (line.equals(prev)) {
+        rlg.lines.add(prev);
+      } else {
+        rlg.lines.add(line);
+        prev = line;
+      }
+      bitOffset += pitch;
+    }
+
+    rlg.compact();
+
+    return rlg;
+  }
+
+  public static RunLengthEncodedGlyph readRasterByBits(
+    byte [] buf, int width, int height,
+    int xOffset, int yOffset)
+  {
+    RunLengthEncodedGlyph rlg = new RunLengthEncodedGlyph(width, height, xOffset, yOffset);
+
+    int bitOffset = 0;
+    RunLengthEncodedLine prev = null;
+    for (int i=0; i<height; i++) {
+      RunLengthEncodedLine line = new RunLengthEncodedLine();
+      line.append(buf, bitOffset, width);
+      bitOffset += width;
+      if (line.equals(prev)) {
+        rlg.lines.add(prev);
+      } else {
+        rlg.lines.add(line);
+        prev = line;
+      }
+    }
+    rlg.compact();
+
+    return rlg;
+  }
+
+  public static RunLengthEncodedGlyph readByteGray(
+    byte [] buf, int width, int height,
+    int xOffset, int yOffset)
+  {
+    RunLengthEncodedGlyph rlg = new RunLengthEncodedGlyph(width, height, xOffset, yOffset);
+
+    int ptr = 0;
+    RunLengthEncodedLine prev = null;
+    for (int i=0; i<height; i++) {
+      final RunLengthEncodedLine line = new RunLengthEncodedLine();
+
+      int last = 314; // A MAGIC value to denote the begin of line.
+      int count = 0;
+      for (int j=0; j<width; j++) {
+        final int c = buf[ptr++];
+        if (c == last) {
+          count++;
+        } else {
+          if (last != 314)
+            line.append(count, (0 != last));
+          count = 1;
+          last = c;
+        }
+      }
+      if (count > 0)
+        line.append(count, (0 != last));
+
+      if (line.equals(prev)) {
+        rlg.lines.add(prev);
+      } else {
+        rlg.lines.add(line);
+        prev = line;
+      }
+    }
+    rlg.compact();
+
+    return rlg;
+  }
+
+
+  public DviRect getBounds()
+  {
+    return new DviRect(-xOffset, -yOffset, width, height);
+  }
+  public int width()   { return width; }
+  public int height()  { return height; }
+  public int xOffset() { return xOffset; }
+  public int yOffset() { return yOffset; }
+
+  public boolean isEmpty()
+  {
+    return (width <= 0 || height <= 0 || lines.size() == 0);
+  }
+
+  public void rasterizeTo(BinaryDevice out)
+  throws DviException
+  {
+    out.save();
+    try {
+      out.translate(-xOffset, -yOffset);
+      if (out.beginRaster(width, height)) {
+      // TODO: use repeat.
+        for (int i=0; i<height; i++) {
+          RunLengthEncodedLine line = lines.get(i);
+          out.beginLine();
+          line.rasterizeTo(out);
+          out.endLine(0);
+        }
+      }
+      out.endRaster();
+    } finally {
+      out.restore();
+    }
+  }
+
+  public void compact()
+  {
+    int lskip = Integer.MAX_VALUE;
+    int rskip = Integer.MAX_VALUE;
+
+    RunLengthEncodedLine prev = null;
+    for (int i=0; i<height; i++) {
+      RunLengthEncodedLine line = lines.get(i);
+      if (prev == line) continue;
+      if (lskip > 0) {
+        if (line.headOn()) {
+          lskip = 0;
+        } else {
+          lskip = Math.min(lskip, line.head());
+        }
+      }
+    }
+
+    prev = null;
+    for (int i=0; i<height; i++) {
+      RunLengthEncodedLine line = lines.get(i);
+      if (prev == line) continue;
+      line.cropHead(lskip);
+      prev = line;
+    }
+    width -= lskip;
+    xOffset -= lskip;
+
+    if (width <= 0) {
+      width = height = 0;
+      lines.clear();
+      xOffset = yOffset = 0;
+      return;
+    }
+
+    prev = null;
+    for (int i=0; i<height; i++) {
+      RunLengthEncodedLine line = lines.get(i);
+      if (prev == line) continue;
+      if (rskip > 0) {
+        if (line.tailOn()) {
+          rskip = 0;
+        } else {
+          rskip = Math.min(rskip, line.tail());
+        }
+      }
+      prev = line;
+    }
+
+    prev = null;
+    for (int i=0; i<height; i++) {
+      RunLengthEncodedLine line = lines.get(i);
+      if (prev == line) continue;
+      line.cropTail(rskip);
+      prev = line;
+    }
+    width -= rskip;
+
+    if (width <= 0) {
+      width = height = 0;
+      lines.clear();
+      xOffset = yOffset = 0;
+      return;
+    }
+
+    int tskip = 0;
+    int bskip = 0;
+
+    while (height > 0) {
+      RunLengthEncodedLine line = lines.get(0);
+      if (!line.allOff())
+        break;
+      lines.remove(0);
+      height--;
+      yOffset--;
+      tskip++;
+    }
+
+    while (height > 0) {
+      RunLengthEncodedLine line = lines.get(height-1);
+      if (!line.allOff())
+        break;
+      lines.remove(height-1);
+      height--;
+      bskip++;
+    }
+
+    for (int i=0; i<height; i++) {
+      RunLengthEncodedLine line = lines.get(i);
+      if (line.isEmpty()) {
+        throw new IllegalStateException
+          ("width=" + width + " height=" + height);
+      }
+    }
+    if (width <= 0 || height <= 0) {
+      throw new IllegalStateException
+        ("width=" + width + " height=" + height);
+    }
+  }
+
+  public String dump()
+  {
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+    for (int i=0; i<height; i++) {
+      RunLengthEncodedLine line = lines.get(i);
+      boolean duplicate = (
+        (i > 0) && lines.get(i-1) == line
+      );
+      pw.println((duplicate ? "+ " : "  " ) + line.dump());
+    }
+    return sw.toString();
+  }
+
+  public String toString()
+  {
+    return getClass().getName()
+      + "[width=" + width
+      + " height=" +height
+      + "]";
+  }
+
+  public PkGlyph toPkGlyph()
+  {
+    if (isEmpty())
+      return PkGlyph.EMPTY;
+
+    ArrayList<Integer> counts = new ArrayList<Integer>(DEFAULT_ARRAY_SIZE);
+
+    int count=0;
+    int repeat=0;
+    RunLengthEncodedLine line = lines.get(0);
+    boolean turnOn = line.headOn();
+    int i = 0;
+    while (line != null) {
+      ArrayList<Integer> data = line.getData();
+      final int ds = data.size();
+      for (int j=0; j<ds; j++) {
+        int c = data.get(j);
+        boolean needFlush = true;
+        if (j == ds - 1) {
+          // at the end of the line.
+          int r = 0;
+          RunLengthEncodedLine next = null;
+          while (++i < height) {
+            next = lines.get(i);
+            if (next != line)
+              break;
+            r++;
+          }
+          needFlush = (
+            next == null ||
+            next.headOn() != line.tailOn()
+          );
+          line = next;
+          if (count > 0) {
+            count += data.get(j) + r * width;
+            // repeat is unchanged.
+          } else {
+            count = data.get(j);
+            repeat = r;
+          }
+        } else {
+          count += c;
+        }
+        if (needFlush) {
+          if (repeat > 0) {
+            counts.add(-repeat);
+            repeat = 0;
+          }
+          counts.add(count);
+          count = 0;
+        }
+      }
+    }
+
+    PackedSequence ps = new SequencePacker(counts).pack();
+
+    return new PkGlyph(
+      width, height,
+      ps.data(), ps.dynF(),
+      turnOn,
+      xOffset, yOffset
+    );
+  }
+
+  public DviRect bounds()
+  throws DviException
+  {
+    if (isEmpty())
+      return DviRect.EMPTY;
+    else
+      return new DviRect(-xOffset, -yOffset, width, height);
+  }
+
+  public void unite(RunLengthEncodedGlyph rlg)
+  {
+    DviRect u = rlg.getBounds().union(getBounds());
+
+    ArrayList<RunLengthEncodedLine> newLines
+      = new ArrayList<RunLengthEncodedLine>(DEFAULT_ARRAY_SIZE);
+    RunLengthEncodedLine last = null;
+    int ey = u.bottom();
+    for (int y = u.top(); y<=ey; y++) {
+      RunLengthEncodedLine a = new RunLengthEncodedLine();
+      RunLengthEncodedLine b = new RunLengthEncodedLine();
+      int ia = y + yOffset;
+      int ib = y + rlg.yOffset;
+
+      if (0 <= ia && ia < height) {
+        int w = u.width();
+        int lpad = -xOffset-u.left();
+        if (lpad > 0) {
+          a.append(lpad, false);
+          w -= lpad;
+        }
+        a.append(lines.get(ia));
+        w -= width;
+        a.append(w, false);
+      } else {
+        a.append(u.width(), false);
+      }
+
+      if (0 <= ib && ib < rlg.height) {
+        int w = u.width();
+        int lpad = -rlg.xOffset-u.left();
+        if (lpad > 0) {
+          b.append(lpad, false);
+          w -= lpad;
+        }
+        b.append(rlg.lines.get(ib));
+        w -= rlg.width;
+        b.append(w, false);
+      } else {
+        b.append(u.width(), false);
+      }
+
+      a = RunLengthEncodedLine.union(a, b);
+
+      if (a.equals(last)) {
+        newLines.add(last);
+      } else {
+        newLines.add(a);
+        last = a;
+      }
+    }
+
+    this.width   = u.width();
+    this.height  = u.height();
+    this.xOffset = -u.x();
+    this.yOffset = -u.y();
+    this.lines = newLines;
+    compact();
+  }
+
+
+  public BinaryDevice getBinaryDevice(DviResolution res)
+  throws DviException
+  {
+    return new BinaryDeviceImpl(res);
+  }
+
+//  private static String dumpCounts(ArrayList<Integer> counts, boolean flag)
+//  {
+//    String str = "";
+//
+//    for (int k=0; k<counts.size(); k++) {
+//      int c = counts.get(k);
+//      
+//      if (c < 0) {
+//        str += "[" + (-c) + "]";
+//      } else if (flag) {
+//        str += String.valueOf(c);
+//        flag = !flag;
+//      } else {
+//        str += "(" + c + ")";
+//        flag = !flag;
+//      }
+//    }
+//
+//    return str;
+//  }
+
+  private class BinaryDeviceImpl
+  extends AbstractDevice
+  implements BinaryDevice
+  {
+    private BinaryDeviceImpl(DviResolution res)
+    {
+      super(res);
+    }
+
+    public void begin()
+    throws DviException
+    {
+    }
+
+    public void end()
+    throws DviException
+    {
+    }
+
+    private RunLengthEncodedGlyph rlg = null;
+    public boolean beginRaster(int w, int h)
+    throws DviException
+    {
+      rlg = new RunLengthEncodedGlyph();
+      rlg.width = w;
+      rlg.height = h;
+      return true;
+    }
+
+    public void endRaster()
+    throws DviException
+    {
+      DviPoint p = getReferencePoint();
+      // The reference point of rlg is at the origin of this device.
+      rlg.xOffset = -p.x;
+      rlg.yOffset = -p.y;
+      rlg.compact();
+
+      unite(rlg);
+      rlg = null;
+    }
+
+    private RunLengthEncodedLine line = null;
+    public void beginLine()
+    throws DviException
+    {
+      line = new RunLengthEncodedLine();
+    }
+
+    public void endLine(int repeat)
+    throws DviException
+    {
+      for (int i=0; i<=repeat; i++) {
+        rlg.lines.add(line);
+      }
+      line = null;
+    }
+
+    public void putBits(int count, boolean paintFlag)
+    {
+      line.append(count, paintFlag);
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/RunLengthEncodedLine.java b/src/jp/sourceforge/dvibrowser/dvicore/font/RunLengthEncodedLine.java
new file mode 100644 (file)
index 0000000..38488bd
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+import java.util.ArrayList;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+
+
+// mutable
+
+public final class RunLengthEncodedLine
+implements Cloneable
+{
+  private int hash = 0;
+  private int width = 0;
+  private boolean headOn = false;
+  private boolean tailOn = false;
+  private ArrayList<Integer> data
+    = new ArrayList<Integer>();
+  
+  // TODO: Make the return value unmodifiable.
+  public ArrayList<Integer> getData() { return data; }
+  public boolean headOn() { return headOn; }
+  public boolean tailOn() { return tailOn; }
+  public int width() { return width; }
+  public boolean allOn() {
+    return headOn && data.size() == 1;
+  }
+  public boolean allOff() {
+    return !headOn && data.size() == 1;
+  }
+  public int head() {
+    return isEmpty() ? 0 : data.get(0);
+  }
+  public int tail() {
+    return isEmpty() ? 0 : data.get(data.size()-1);
+  }
+  ///////////////
+
+  public void rasterizeTo(BinaryDevice out)
+  throws DviException
+  {
+    boolean flag = headOn;
+    final int ds = data.size();
+    for (int i=0; i<ds; i++) {
+      out.putBits(data.get(i), flag);
+      flag = !flag;
+    }
+  }
+
+  ///////////////
+  
+  public void prepend(int count, boolean isOn)
+  {
+    if (count <= 0) return;
+    if (width == 0) {
+      headOn = tailOn = isOn;
+      data.add(count);
+      width = count;
+    } else {
+      if (headOn == isOn) {
+        data.set(0, data.get(0) + count);
+      } else {
+        data.add(0, count);
+        headOn = isOn;
+      }
+      width += count;
+    }
+    hash += isOn ? count : -count;
+  }
+
+  public void append(int count, boolean isOn)
+  {
+    if (count <= 0) return;
+    if (width == 0) {
+      headOn = tailOn = isOn;
+      data.add(count);
+      width = count;
+    } else {
+      int last = data.size() - 1;
+      if (tailOn == isOn) {
+        data.set(last, data.get(last) + count);
+      } else {
+        data.add(count);
+        tailOn = isOn;
+      }
+      width += count;
+    }
+    hash += isOn ? count : -count;
+  }
+
+  public void cropHead(int count) {
+    if (width == 0) return;
+    while (count > 0) {
+      int c = data.get(0).intValue();
+      if (c > count) {
+        data.set(0, c - count);
+        width -= count;
+        hash -= headOn ? count : -count;
+        break;
+      } else {
+        data.remove(0);
+        hash -= headOn ? c : -c;
+        headOn = !headOn;
+        count -= c;
+        width -= c;
+        if (width <= 0) {
+          clear();
+          break;
+        }
+      }
+    }
+  }
+
+  public void cropTail(int count)
+  {
+    if (width == 0) return;
+    while (count > 0) {
+      int last = data.size() - 1;
+      int c = data.get(last);
+      if (c > count) {
+        data.set(last, c - count);
+        hash -= tailOn ? count : -count;
+        width -= count;
+        break;
+      } else {
+        data.remove(last);
+        hash -= tailOn ? c : -c;
+        tailOn = !tailOn;
+        count -= c;
+        width -= c;
+        if (width <= 0) {
+          clear();
+          break;
+        }
+      }
+    }
+  }
+
+  ///////////////
+
+  public void prepend(RunLengthEncodedLine rll)
+  {
+    if (rll.isEmpty()) return;
+    if (rll == this)
+      rll = (RunLengthEncodedLine) clone();
+    boolean flag = rll.tailOn;
+    for (int i=rll.data.size()-1; i>=0; i--) {
+      prepend(rll.data.get(i), flag);
+      flag = !flag;
+    }
+  }
+
+  public void append(RunLengthEncodedLine rll)
+  {
+    if (rll.isEmpty()) return;
+    if (rll == this)
+      rll = (RunLengthEncodedLine) clone();
+    boolean flag = rll.headOn;
+    final int ds = rll.data.size();
+    for (int i=0; i<ds; i++) {
+      append(rll.data.get(i), flag);
+      flag = !flag;
+    }
+  }
+
+  ///////////////
+  
+  public void prepend(byte [] buf) {
+    prepend(buf, 0, buf.length * 8, false);
+  }
+  public void append(byte [] buf) {
+    append(buf, 0, buf.length * 8, false);
+  }
+  public void prepend(byte [] buf, int bitOffset, int bitLen) {
+    prepend(buf, bitOffset, bitLen, false);
+  }
+  public void append(byte [] buf, int bitOffset, int bitLen) {
+    append(buf, bitOffset, bitLen, false);
+  }
+  public void prepend(byte [] buf, int bitOffset, int bitLen, boolean revert)
+  {
+    if (bitOffset < 0)
+      throw new IllegalArgumentException
+        ("bit offset can't be negative");
+    if (bitLen <= 0) return;
+    // TODO: Optimize this code using a look-up table.
+    for (int i=bitOffset+bitLen-1; i>=bitOffset; i--) {
+      boolean flag = (0 != (buf[i >>> 3] & (1 << (7 - (i & 7)))));
+      prepend(1, revert ? !flag : flag);
+    }
+  }
+  
+  public void append(byte [] buf,
+    int bitOffset, int bitLen, boolean revert)
+  {
+    if (bitOffset < 0)
+      throw new IllegalArgumentException
+        ("bit offset can't be negative");
+    if (bitLen <= 0) return;
+    // TODO: Optimize this code using a look-up table.
+    for (int i=0; i<bitLen; i++) {
+      final int p = i + bitOffset;
+      boolean flag = (0 != (buf[p >>> 3] & (1 << (7 - (p & 7)))));
+      append(1, revert ? !flag : flag);
+    }
+  }
+
+  ///////////////
+
+  public static RunLengthEncodedLine union(RunLengthEncodedLine a, RunLengthEncodedLine b)
+  {
+    if (a == null || b == null)
+      throw new NullPointerException();
+    if (a.isEmpty()) return (RunLengthEncodedLine) b.clone();
+    if (b.isEmpty()) return (RunLengthEncodedLine) a.clone();
+
+    RunLengthEncodedLine rll = new RunLengthEncodedLine();
+    boolean a_flag = a.headOn;
+    boolean b_flag = b.headOn;
+    int a_ptr = 0;
+    int b_ptr = 0;
+    int a_size = a.data.size();
+    int b_size = b.data.size();
+    int a_count = a.data.get(0);
+    int b_count = b.data.get(0);
+    while (a_count != Integer.MAX_VALUE ||
+           b_count != Integer.MAX_VALUE)
+    {
+      final int count;
+      boolean flag = a_flag || b_flag;
+      if (a_flag && b_flag) {
+        count = Math.max(a_count, b_count);
+      } else if (a_flag) {
+        count = a_count;
+      } else if (b_flag) {
+        count = b_count;
+      } else {
+        count = Math.min(a_count, b_count);
+      }
+
+      rll.append(count, flag);
+
+      {
+        int c = count;
+        while (a_count <= c) {
+          c -= a_count;
+          if (++a_ptr >= a_size) {
+            a_count = Integer.MAX_VALUE;
+            a_flag = false;
+          } else {
+            a_count = a.data.get(a_ptr);
+            a_flag = !a_flag;
+          }
+        }
+        if (a_count < Integer.MAX_VALUE)
+          a_count -= c;
+      }
+
+      {
+        int c = count;
+        while (b_count <= c) {
+          c -= b_count;
+          if (++b_ptr >= b_size) {
+            b_count = Integer.MAX_VALUE;
+            b_flag = false;
+          } else {
+            b_count = b.data.get(b_ptr);
+            b_flag = !b_flag;
+          }
+        }
+        if (b_count < Integer.MAX_VALUE)
+          b_count -= c;
+      }
+    }
+
+    return rll;
+  }
+  
+  ///////////////
+
+  public void clear()
+  {
+    hash = 0;
+    width = 0;
+    headOn = tailOn = false;
+    data.clear();
+  }
+
+  public boolean isEmpty()
+  {
+    return (width == 0);
+  }
+
+  public boolean equals(Object obj)
+  {
+    if (this == obj) return true;
+    if (obj instanceof RunLengthEncodedLine) {
+      RunLengthEncodedLine rll = (RunLengthEncodedLine) obj;
+      return rll.hash   == hash
+          && rll.width  == width
+          && rll.headOn == headOn
+          && rll.tailOn == tailOn
+          && rll.data.equals(data)
+          ;
+    }
+
+    return false;
+  }
+
+  public int hashCode()
+  {
+    return hash;
+  }
+
+  @SuppressWarnings({"unchecked"})
+  public Object clone()
+  {
+    try {
+      RunLengthEncodedLine rll = (RunLengthEncodedLine) super.clone();
+      rll.data = (ArrayList<Integer>) rll.data.clone();
+      return rll;
+    } catch (CloneNotSupportedException ex) {
+      throw new InternalError(); // this shouldn't happen.
+    }
+  }
+
+  public String toString()
+  {
+    StringBuilder sb = new StringBuilder();
+    boolean flag = headOn;
+    int s = data.size();
+    for (int i=0; i<s; i++) {
+      int count = data.get(i);
+      if (flag)
+        sb.append(String.valueOf(count));
+      else
+        sb.append("(" + String.valueOf(count) + ")");
+      flag = !flag;
+    }
+    return getClass().getName()
+      + "[width=" + width
+      + " hash=" + hash
+      + " headOn=" + headOn
+      + " tailOn=" + tailOn
+      + " dataSize=" + data.size()
+      + " data=[" + sb.toString() + "]"
+      ;
+  }
+
+  public String dump()
+  {
+    StringBuilder sb = new StringBuilder();
+    boolean flag = headOn;
+    int s = data.size();
+    for (int i=0; i<s; i++) {
+      int count = data.get(i);
+      if (flag)
+        while (count-- > 0)
+          sb.append("*");
+      else
+        while (count-- > 0)
+          sb.append("-");
+      flag = !flag;
+    }
+    return sb.toString();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/SequencePacker.java b/src/jp/sourceforge/dvibrowser/dvicore/font/SequencePacker.java
new file mode 100644 (file)
index 0000000..21da8be
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+import java.util.ArrayList;
+
+
+public class SequencePacker
+{
+  private final ArrayList<Integer> data; // We don't modify its content.
+
+  public SequencePacker(ArrayList<Integer> data)
+  {
+    this.data = data;
+  }
+
+  private byte [] _buf;
+  private int _ptr;
+  private int _highNyb;
+
+  private void writeNyb(int a)
+  {
+    if (_highNyb != 0) {
+      _buf[_ptr] |= (byte)((a & 15) << 4);
+      _highNyb = 0;
+    } else {
+      _buf[_ptr] |= (byte)(a & 15);
+      _ptr++;
+      _highNyb = 1;
+    }
+  }
+
+  public PackedSequence pack()
+  {
+    Config cfg = findBestConfig(data);
+    return pack(cfg);
+  }
+
+  private PackedSequence pack(final Config cfg)
+  {
+    final int nybLength = cfg.nybLength;
+    final byte [] buf = new byte [(nybLength + 1)>>>1];
+    final int dynF = cfg.dynF;
+    final int dynG = computeDynG(dynF);
+    final int dynH = computeDynH(dynF);
+
+    _buf = buf;
+    _ptr = 0;
+    _highNyb = 1;
+
+    final int ds = data.size();
+    for (int i=0; i<ds; i++) {
+      int c = data.get(i);
+      if (c == 0) continue; // ignored.
+
+      if (c == -1) {
+        writeNyb(15);
+        continue;
+      } else if (c < 0) {
+        writeNyb(14);
+        c = -c;
+      }
+
+      if (c <= dynF) {
+        writeNyb(c);
+      } else if (c < dynG) {
+        final int cc = c + dynH;
+        writeNyb(cc >>> 4);
+        writeNyb(cc >>> 0);
+      } else {
+        final int cc = c - dynG + 16;
+        int k = cc >>> 4;
+        int nl = 0;
+        while (k > 0) {
+          writeNyb(0);
+          k >>>= 4;
+          nl += 4;
+        }
+        while (nl >= 0) {
+          writeNyb(cc >>> nl);
+          nl -= 4;
+        }
+      }
+    }
+
+    _buf = null;
+
+    return new PackedSequence(buf, dynF, nybLength);
+  }
+
+  private static int computeDynG(int dynF)
+  {
+    return ((13-dynF) << 4) + dynF + 1;
+  }
+
+  private static int computeDynH(int dynF)
+  {
+    return ((dynF+1) << 4) - dynF - 1; // 15 * (dynF + 1)
+  }
+
+  private static final int [] cc2diffPos;
+
+  static {
+    cc2diffPos = new int[13*15];
+    for (int d=0; d<13; d++) {
+      for (int j=0; j<15; j++) {
+        int cc = d * 15 + j;
+        cc2diffPos[cc] = 12 - d;
+      }
+    }
+  }
+
+  private static class Config
+  {
+    private final int dynF;
+    private final int nybLength;
+    private Config(int dynF, int nybLength) {
+      this.dynF = dynF;
+      this.nybLength = nybLength;
+    }
+  }
+
+  private static Config findBestConfig(ArrayList<Integer> data)
+  {
+    int len = 0;
+    int [] diff = new int [13];
+
+    // We first compute the nyb length, assuming that dynF=0.
+    // At the same time, we store the difference information to diff,
+    // which is used later to compute the nyb length for other
+    // values of dynF.
+    final int ds = data.size();
+    for (int i=0; i<ds; i++) {
+      int c = data.get(i);
+      if (c == 0) continue; // ignored.
+      if (c == -1) {
+        len++;
+        continue;
+      } else if (c < 0) {
+        len++;
+        c = -c;
+      }
+
+      if (c < (1 << 4) - 2) {
+        diff[c-1] -= 1;
+        len += 2;
+      } else if (c < (1 <<  4) - 2 + 195) {
+        final int cc = c - ((1 << 4) - 2);
+        diff[cc2diffPos[cc]] += 1;
+        len += 2;
+      } else if (c < (1 <<  8) - 2) {
+        len += 3;
+      } else if (c < (1 <<  8) - 2 + 195) {
+        final int cc = c - ((1 << 8) - 2);
+        diff[cc2diffPos[cc]] += 2;
+        len += 3;
+      } else if (c < (1 << 12) - 2) {
+        len += 5;
+      } else if (c < (1 << 12) - 2 + 195) {
+        final int cc = c - ((1 << 12) - 2);
+        diff[cc2diffPos[cc]] += 2;
+        len += 5;
+      } else if (c < (1 << 16) - 2) {
+        len += 7;
+      } else if (c < (1 << 16) - 2 + 195) {
+        final int cc = c - ((1 << 16) - 2);
+        diff[cc2diffPos[cc]] += 2;
+        len += 7;
+      } else if (c < (1 << 20) - 2) {
+        len += 9;
+      } else if (c < (1 << 20) - 2 + 195) {
+        final int cc = c - ((1 << 20) - 2);
+        diff[cc2diffPos[cc]] += 2;
+        len += 9;
+      } else if (c < (1 << 24) - 2) {
+        len += 11;
+      } else if (c < (1 << 24) - 2 + 195) {
+        final int cc = c - ((1 << 24) - 2);
+        diff[cc2diffPos[cc]] += 2;
+        len += 11;
+      } else if (c < (1 << 28) - 2) {
+        len += 13;
+      } else if (c < (1 << 28) - 2 + 195) {
+        final int cc = c - ((1 << 28) - 2);
+        diff[cc2diffPos[cc]] += 2;
+        len += 13;
+      } else {
+        len += 15;
+      }
+    }
+
+    int bestDynF = -1;
+    int bestSize = Integer.MAX_VALUE;
+    int dynF = 0;
+    for (;;) {
+      if (len < bestSize) {
+        bestDynF = dynF;
+        bestSize = len;
+      }
+      if (dynF >= 13)
+        break;
+
+      len += diff[dynF];
+      dynF++;
+    }
+
+    Config cfg = new Config(bestDynF, bestSize);
+    return cfg;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/TexFontMetrics.java b/src/jp/sourceforge/dvibrowser/dvicore/font/TexFontMetrics.java
new file mode 100644 (file)
index 0000000..3b8c334
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+
+import jp.sourceforge.dvibrowser.dvicore.DviConstants;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+import jp.sourceforge.dvibrowser.dvicore.api.FullMetrics;
+import jp.sourceforge.dvibrowser.dvicore.io.DviInputStreamReader;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+// TODO: support Tategumi
+
+public class TexFontMetrics
+extends DviObject
+implements FullMetrics
+{
+  private static final long serialVersionUID = 962603909880917980L;
+
+  // TODO: outsource JFM configuration
+  private boolean enableJFM = true;
+
+  private boolean isJFM; // For Japanese pTeX
+
+private int checkSum;
+  private int designSize;
+
+  private final String name;
+
+  private final HashMap<Integer, Record> ct2rec
+    = new HashMap<Integer, Record>();
+  private HashMap<Integer, Integer> code2ct;
+
+  public TexFontMetrics(DviContextSupport dcs, File file)
+  throws DviException
+  {
+    super(dcs);
+    FileInputStream is = null;
+    try {
+      is = new FileInputStream(file);
+      parseInputStream(is);
+    } catch (IOException ex) {
+      DviUtils.silentClose(is);
+      throw new DviException(ex);
+    }
+    name = getClass().getName()
+      + "--" + file.getPath()
+      + "--" + file.lastModified()
+      ;
+  }
+
+  public TexFontMetrics(DviContextSupport dcs, InputStream is)
+  throws DviException
+  {
+    super(dcs);
+    try {
+      parseInputStream(is);
+    } catch (IOException ex) {
+      throw new DviException(ex);
+    }
+    name = getClass().getName()
+      + "--" + is
+      + "--" + System.currentTimeMillis()
+      ;
+  }
+
+  @SuppressWarnings("unused")
+  protected void parseInputStream(InputStream is)
+  throws IOException, DviException
+  {
+    DviInputStreamReader in = new DviInputStreamReader(
+      new BufferedInputStream(is, 1024)
+    );
+
+    long start = in.getOffset();
+
+    int id; // For Japanese pTeX
+    int nt; // For Japanese pTeX
+    int lf;
+    int lh;
+    int bc;
+    int ec;
+    int nw;
+    int nh;
+    int nd;
+    int ni;
+    int nl;
+    int nk;
+    int ne;
+    int np;
+
+    int numChars;
+
+    if (enableJFM) {
+      id = in.readU2();
+      nt = in.readU2();
+      isJFM = (id == DviConstants.TFM_ID_TATEGUMI || id == DviConstants.TFM_ID_YOKOGUMI);
+      if (isJFM) {
+        lf = in.readU2();
+        lh = in.readU2();
+      } else {
+        lf = id;
+        lh = nt;
+        id = 0;
+        nt = 0;
+      }
+    } else {
+      id = 0;
+      nt = 0;
+      lf = in.readU2();
+      lh = in.readU2();
+      isJFM = false;
+    }
+
+    bc = in.readU2();
+    ec = in.readU2();
+    nw = in.readU2();
+    nh = in.readU2();
+    nd = in.readU2();
+    ni = in.readU2();
+    nl = in.readU2();
+    nk = in.readU2();
+    ne = in.readU2();
+    np = in.readU2();
+    numChars = ec - bc + 1;
+
+    // header
+    int [] header_t = readBuffer(in, lh);
+    {
+      checkSum   = header_t[0];
+      designSize = header_t[1];
+    }
+
+    // character type map (for Japanese JFM)
+    if (isJFM) {
+      code2ct = new HashMap<Integer, Integer>();
+      for (int i=0; i<nt; i++) {
+        final int from = in.readU2();
+        final int   to = in.readU2();
+        code2ct.put(from, to);
+      }
+    } else {
+      code2ct = null;
+    }
+
+    int [] char_info_t = readBuffer(in, numChars);
+    int []     width_t = readBuffer(in, nw);
+    int []    height_t = readBuffer(in, nh);
+    int []     depth_t = readBuffer(in, nd);
+    int []    italic_t = readBuffer(in, ni);
+    int []  lig_kern_t = readBuffer(in, nl);
+    int []      kern_t = readBuffer(in, nk);
+    int []     exten_t = readBuffer(in, ne);
+    int []     param_t = readBuffer(in, np);
+
+    long size = in.getOffset() - start;
+    if (lf * 4 != size ||
+        lf != (isJFM ? 7 : 6) +
+                           lh +
+                           nt +
+                     numChars +
+                           nw +
+                           nh +
+                           nd +
+                           ni +
+                           nl +
+                           nk +
+                           ne +
+                           np)
+        throw new DviException
+          ("Length mismatch in TFM data");
+
+    if (isJFM && bc != 0)
+      throw new DviException
+        ("bc must be 0 in JFM data.");
+
+    for (int i=0; i<numChars; i++) {
+      final int  ct = i + bc;
+      final int  ci = char_info_t[i];
+      final int  wi = (ci >>> 24) & 0xff;
+      final int  hi = (ci >>> 20) & 0x0f;
+      final int  di = (ci >>> 16) & 0x0f;
+      final int  ii = (ci >>> 10) & 0x3f;
+      final int tag = (ci >>> 8)  & 0x03;
+      final int rem = (ci >>> 0)  & 0xff;
+
+      final int  width = width_t [wi];
+      final int height = height_t[hi];
+      final int  depth = depth_t [di];
+      final int italic = italic_t[ii];
+
+      ct2rec.put(ct, new Record(width, height, depth, italic));
+    }
+  }
+
+  private static int [] readBuffer(DviInput in, int length)
+  throws IOException
+  {
+    int [] buf = new int[length];
+    for (int i=0; i<length; i++)
+      buf[i] = in.readU4();
+    return buf;
+  }
+
+
+  public String getName()
+  {
+    return name;
+  }
+  
+  public int getCheckSum() {
+         return checkSum;
+  }
+
+  public int getDesignSize() {
+         return designSize;
+  }
+
+  public boolean isJFM() {
+         return isJFM;
+  }
+
+  public boolean hasChar(int code)
+  {
+    return isJFM ? true : ct2rec.containsKey(code);
+  }
+
+  private int codeToCt(int code)
+  {
+    return (code2ct == null) ? code :
+      code2ct.containsKey(code) ? code2ct.get(code) : 0;
+  }
+  private Record getRecord(int code)
+  {
+    return ct2rec.get(codeToCt(code));
+  }
+
+  public int getTfmWidth(int code)
+  {
+    Record rec = getRecord(code);
+    return (rec != null) ? rec.width : 0;
+  }
+  public int getTfmHeight(int code)
+  {
+    Record rec = getRecord(code);
+    return (rec != null) ? rec.height : 0;
+  }
+  public int getTfmDepth(int code)
+  {
+    Record rec = getRecord(code);
+    return (rec != null) ? rec.depth : 0;
+  }
+  public int getTfmItalic(int code)
+  {
+    Record rec = getRecord(code);
+    return (rec != null) ? rec.italic : 0;
+  }
+  
+  public static String getTfmFilename(DviFontSpec spec)
+  {
+    String tfm = spec.name() + ".tfm";
+    return tfm;
+  }
+
+  private static class Record implements java.io.Serializable {
+    private static final long serialVersionUID = 1638947545775532119L;
+    private final int width;
+    private final int height;
+    private final int depth;
+    private final int italic;
+
+    private Record(int width, int height, int depth, int italic) {
+      this.width = width;
+      this.height = height;
+      this.depth = depth;
+      this.italic = italic;
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/TrueTypeFontResolver.java b/src/jp/sourceforge/dvibrowser/dvicore/font/TrueTypeFontResolver.java
new file mode 100644 (file)
index 0000000..2378108
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.CharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+import jp.sourceforge.dvibrowser.dvicore.ctx.UnicodeCharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+// TODO: merge this class with Type1FontResolver
+public class TrueTypeFontResolver
+extends AbstractDviFontResolver
+{
+  private static final Logger LOGGER = Logger.getLogger(DviFontResolver.class
+      .getName());
+  
+  private static final CharacterCodeMapper mapper = new UnicodeCharacterCodeMapper();
+  
+  private String fileExtension = ".ttf";
+
+  public TrueTypeFontResolver(DviContextSupport dcs, LogicalFont logicalFont)
+  {
+    super(dcs, logicalFont);
+  }
+
+  @Override
+  protected DviFont createInstanceFromStream(InputStream is) throws DviException
+  {
+    try {
+      Font font = Font.createFont(Font.TRUETYPE_FONT, is);
+      return new AWTDynamicPkFont(this, font, mapper);
+    } catch (IOException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    } catch (FontFormatException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    }
+  }
+
+  @Override
+  protected String mapToDviResourceName(LogicalFont logicalFont)
+  {
+    String name = logicalFont.fontSpec().name();
+    return name + fileExtension;
+  }
+
+  public void setFileExtension(String fileExtension) {
+    this.fileExtension = fileExtension;
+  }
+
+  public String getFileExtension() {
+    return fileExtension;
+  }
+
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/Type1FontResolver.java b/src/jp/sourceforge/dvibrowser/dvicore/font/Type1FontResolver.java
new file mode 100644 (file)
index 0000000..26a5fdd
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.CharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+import jp.sourceforge.dvibrowser.dvicore.ctx.UnicodeCharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+public class Type1FontResolver
+extends AbstractDviFontResolver
+{
+  private static final Logger LOGGER = Logger.getLogger(DviFontResolver.class
+      .getName());
+  
+  private static final CharacterCodeMapper mapper = new UnicodeCharacterCodeMapper();
+  
+  public Type1FontResolver(DviContextSupport dcs, LogicalFont logicalFont)
+  {
+    super(dcs, logicalFont);
+  }
+
+  @Override
+  protected DviFont createInstanceFromStream(InputStream is) throws DviException
+  {
+    try {
+      Font font = Font.createFont(Font.TYPE1_FONT, is);
+      return new AWTDynamicPkFont(this, font, mapper);
+    } catch (IOException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    } catch (FontFormatException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    }
+  }
+
+  @Override
+  protected String mapToDviResourceName(LogicalFont logicalFont)
+  {
+    String name = logicalFont.fontSpec().name();
+    return name + ".pfb";
+  }
+
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/VirtualFont.java b/src/jp/sourceforge/dvibrowser/dvicore/font/VirtualFont.java
new file mode 100644 (file)
index 0000000..10b95be
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+
+import java.io.BufferedInputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+
+import jp.sourceforge.dvibrowser.dvicore.DviConstants;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviFontTable;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+import jp.sourceforge.dvibrowser.dvicore.api.DevicePainter;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutor;
+import jp.sourceforge.dvibrowser.dvicore.api.Geometer;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviCommand;
+import jp.sourceforge.dvibrowser.dvicore.io.ByteArrayDviData;
+import jp.sourceforge.dvibrowser.dvicore.io.DviInputStreamReader;
+import jp.sourceforge.dvibrowser.dvicore.render.VirtualFontGeometer;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+public class VirtualFont
+extends AbstractDynamicPkFont
+{
+  private static final long serialVersionUID = -4723597208297552498L;
+
+  private final HashMap<Integer, GlyphInfo> glyphInfos
+    = new HashMap<Integer, GlyphInfo>();
+  
+  private final String name;
+
+  public VirtualFont(DviContextSupport dcs, File file)
+  throws DviException
+  {
+    super(dcs);
+    try {
+      FileInputStream fis = null;
+      try {
+        fis = new FileInputStream(file);
+        parseInputStream(fis);
+      } finally {
+        DviUtils.silentClose(fis);
+      }
+    } catch (IOException ex) {
+      throw new DviException(ex);
+    }
+    name = getClass().getName()
+      + "--" + file
+      + "--" + file.lastModified()
+      + "--[" + new String(comment) + "]"
+      ;
+  }
+
+  public VirtualFont(DviContextSupport dcs, InputStream is)
+  throws DviException
+  {
+    super(dcs);
+    try {
+      parseInputStream(is);
+    } catch (IOException ex) {
+      throw new DviException(ex);
+    }
+    name = getClass().getName()
+      + "--" + is
+      + "--" + System.currentTimeMillis()
+      + "--[" + new String(comment) + "]"
+      ;
+  }
+
+
+  private       int idByte;
+  private   byte [] comment;
+  private       int checkSum;
+  private       int designSize;
+  private DviFontTable fontTable;
+  private       int defaultFontNumber = DviConstants.UNDEFINED_FONT_NUMBER;
+
+  public boolean hasChar(int code)
+  throws DviException
+  {
+    return glyphInfos.containsKey(code);
+  }
+
+  public String getName()
+  {
+    return name;
+  }
+
+  public int getIdByte() {
+         return idByte;
+  }
+
+  public byte[] getComment() {
+         return comment.clone();
+  }
+
+  public int getCheckSum() {
+         return checkSum;
+  }
+
+  public int getDesignSize() {
+         return designSize;
+  }
+
+  public int getDefaultFontNumber() {
+         return defaultFontNumber;
+  }
+  
+  private void parseInputStream(InputStream is)
+  throws IOException, DviException
+  {
+    DviInputStreamReader in
+      = new DviInputStreamReader(
+        new BufferedInputStream(is, 1024)
+      );
+    try {
+      if (DviCommand.DVI_PRE != in.readU1())
+        throw new DviException
+          ("VF data doesn't start with PRE");
+
+      idByte = in.readU1();
+      if (idByte != DviConstants.VF_ID_BYTE)
+        throw new DviException
+          ("unknown id byte: " + idByte);
+
+      int commentSize = in.readU1();
+      comment = new byte [commentSize];
+      in.readFully(comment);
+
+      checkSum   = in.readS4();
+      designSize = in.readS4();
+
+      int c;
+
+      fontTable = new DviFontTable();
+
+      boolean stop = false;
+      do {
+        c = in.readU1();
+
+        switch (c) {
+          case DviCommand.DVI_FNT_DEF4:
+          case DviCommand.DVI_FNT_DEF3:
+          case DviCommand.DVI_FNT_DEF2:
+          case DviCommand.DVI_FNT_DEF1: {
+            int t = c - DviCommand.DVI_FNT_DEF1 + 1;
+            int fn, cs, ss, ds, al, nl;
+            byte [] fontName;
+
+            fn = in.readS(t);
+            cs = in.readS4();
+            ss = in.readS4();
+            ds = in.readS4();
+            al = in.readU1();
+            nl = in.readU1();
+  
+            if (defaultFontNumber == DviConstants.UNDEFINED_FONT_NUMBER)
+              defaultFontNumber = fn;
+  
+            fontName = new byte[al+nl];
+            in.readFully(fontName);
+
+            fontTable.put(
+              fn,
+              DviFontSpec.getInstance(
+                cs, ss, ds, al, nl, fontName
+              )
+            );
+            break;
+          }
+          default:
+            stop = true;
+        }
+      } while (!stop);
+
+      while (true) {
+//        int cmdLength = 1;
+    
+        GlyphInfo gi = new GlyphInfo();
+        int macroLength;
+//        int packetLength;
+    
+        if (c < 242) {  // short chararacter
+          gi.type = DviConstants.VF_SHORT_CHAR;
+          macroLength = c;
+//          packetLength = c + 5;
+          gi.code = in.readU1();
+          gi.tfmw = in.readS3();
+          gi.macro = new byte [macroLength];
+          in.readFully(gi.macro);
+        } else if (c == 242) { // long character
+          gi.type = DviConstants.VF_LONG_CHAR;
+          macroLength = in.readS4();
+//          packetLength = macroLength + 13;
+          gi.code = in.readS4();
+          gi.tfmw = in.readS4();
+          gi.macro = new byte [macroLength];
+          in.readFully(gi.macro);
+        } else if (c == DviCommand.DVI_POST) {
+          break;
+        } else {
+          throw new DviException
+            ("VF data doesn't terminate with POST");
+        }
+        glyphInfos.put(gi.code, gi);
+
+        c = in.readU1();
+      }
+    } catch (EOFException ex) {
+      throw new DviException
+        ("VF file ended while reading glyphs.", ex);
+    } catch (IOException ex) {
+      throw new DviException(ex);
+    }
+  }
+
+  private static class GlyphInfo
+  implements java.io.Serializable
+  {
+       private static final long serialVersionUID = -3968612702338551000L;
+       private int type;
+    private int code;
+    private int tfmw;
+    private byte [] macro;
+
+    public String toString() {
+      return getClass().getName()
+        + "[type=" + type
+        + " code=" + code
+        + " tfwm=" + tfmw
+        + " macroLength=" + macro.length
+        + "]";
+    }
+  }
+
+  protected PkGlyph generatePkGlyph(LogicalFont lf, int code)
+  throws DviException
+  {
+    GlyphInfo gi = glyphInfos.get(code);
+    if (gi == null) return null;
+
+    DviResolution res = lf.resolution();
+
+    RunLengthEncodedGlyph rlg = new RunLengthEncodedGlyph();
+    BinaryDevice dev = rlg.getBinaryDevice(res);
+    // This is a trick to use VirtualFontGeometer.
+    dev.translate(-res.dpi(), -res.dpi());
+
+    DviExecutor exe = getDviContext().newDviExecutor();
+    DevicePainter dp = getDviContext().newDevicePainter();
+    dp.setOutput(dev);
+    Geometer geometer = new VirtualFontGeometer(this, lf.fontSpec());
+    geometer.setPainter(dp);
+    DviFontTable ft =
+      fontTable.transformForVirtualFont(
+        lf.fontSpec(), designSize
+      );
+  
+    {
+      final byte [] buf = new byte[gi.macro.length + 1 + 4];
+      final int fn = defaultFontNumber;
+      buf[0] = (byte) DviCommand.DVI_FONT4;
+      buf[1] = (byte)((fn >>> 24) & 0xff);
+      buf[2] = (byte)((fn >>> 16) & 0xff);
+      buf[3] = (byte)((fn >>>  8) & 0xff);
+      buf[4] = (byte)((fn >>>  0) & 0xff);
+      System.arraycopy(gi.macro, 0, buf, 5, gi.macro.length);
+  
+      exe.execute(
+        new ByteArrayDviData(
+          buf, lf.dviUnit(), ft
+        ),
+         geometer
+      );
+    }
+
+    return rlg.toPkGlyph();
+  }
+  
+  public static String getDviResourceName(LogicalFont logicalFont)
+  {
+    return logicalFont.fontSpec().name() + ".vf";
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/font/VirtualFontResolver.java b/src/jp/sourceforge/dvibrowser/dvicore/font/VirtualFontResolver.java
new file mode 100644 (file)
index 0000000..48e348d
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.font;
+import java.io.InputStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+
+
+public class VirtualFontResolver
+extends AbstractDviFontResolver
+{
+//  private static final Logger LOGGER = Logger.getLogger(DviFontResolver.class
+//      .getName());
+  public VirtualFontResolver(DviContextSupport dcs, LogicalFont logicalFont)
+  {
+    super(dcs, logicalFont);
+  }
+
+  @Override
+  protected DviFont createInstanceFromStream(InputStream openStream) throws DviException
+  {
+    return new VirtualFont(this, openStream);
+  }
+
+  @Override
+  protected String mapToDviResourceName(LogicalFont logicalFont)
+  {
+    return VirtualFont.getDviResourceName(logicalFont);
+  }
+
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/gs/GhostscriptBBOXParser.java b/src/jp/sourceforge/dvibrowser/dvicore/gs/GhostscriptBBOXParser.java
new file mode 100644 (file)
index 0000000..ecac366
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.gs;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.util.CommandShellHandler;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+public class GhostscriptBBOXParser implements CommandShellHandler {
+  private static final Logger LOGGER = Logger
+      .getLogger(GhostscriptBBOXParser.class.getName());
+
+  private final ArrayList<DviRect> list = new ArrayList<DviRect>();
+  
+  private static final Pattern patPostscriptBoundingBox = Pattern.compile
+  ("%%BoundingBox:\\s*([+-]?\\d\\d*)\\s\\s*([+-]?\\d\\d*)\\s\\s*([+-]?\\d\\d*)\\s\\s*([+-]?\\d\\d*)\\s*");
+  
+  public static DviRect parsePostscriptBoundingBox(String line)
+  {
+    Matcher mat = patPostscriptBoundingBox.matcher(line);
+    if (mat.matches()) {
+      int llx = Integer.parseInt(mat.group(1));
+      int lly = Integer.parseInt(mat.group(2));
+      int urx = Integer.parseInt(mat.group(3));
+      int ury = Integer.parseInt(mat.group(4));
+      return DviRect.getDviRect(llx, lly, urx, ury);
+    }
+    return null;
+  }
+  
+  public GhostscriptBBOXParser()
+  {
+  }
+  
+  public int getTotalPages() {
+    return list.size();
+  }
+  
+  public DviRect getBoundingBoxOfPage(int page)
+  {
+    return list.get(page);
+  }
+
+  public void handleStderr(InputStream in) throws IOException {
+    try {
+      Scanner s = new Scanner(in);
+      while (s.hasNext()) {
+        String line = s.nextLine();
+        DviRect bbox = parsePostscriptBoundingBox(line);
+        if (bbox != null) {
+          list.add(bbox);
+        }
+      }
+    } finally {
+      DviUtils.silentClose(in);
+    }
+  }
+
+  public void handleStdin(OutputStream out) throws IOException {
+    out.close();
+  }
+
+  public void handleStdout(InputStream in) throws IOException {
+    in.close();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/gs/GhostscriptCommandBuilder.java b/src/jp/sourceforge/dvibrowser/dvicore/gs/GhostscriptCommandBuilder.java
new file mode 100644 (file)
index 0000000..fa89b43
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.gs;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.util.CommandShell;
+
+
+public class GhostscriptCommandBuilder
+extends DviObject
+{
+  private static final Logger LOGGER = Logger.getLogger(GhostscriptCommandBuilder.class.getName());
+
+  public GhostscriptCommandBuilder(DviContextSupport dcs) {
+    super(dcs);
+  }
+  
+  private String dev = "png256";
+  public void setDevice(String dev)
+  {
+    this.dev = dev;
+  }
+
+  private File outputFile;
+  public void setOutputFile(File outputFile)
+  {
+    this.outputFile = outputFile;
+  }
+
+  private File inputFile;
+  public void setInputFile(File inputFile)
+  {
+    this.inputFile = inputFile;
+  }
+  
+  
+
+  private int dpi = 300;
+  public void setResolution(int dpi)
+  {
+    this.dpi = dpi;
+  }
+  
+  private int firstPage = -1;
+  private int lastPage = -1;
+
+  private DviSize size = null;
+  public void setPaperSize(DviSize size)
+  {
+    this.size = size;
+  }
+  
+  private String gsPath = null;
+  public String getGhostscriptExecutablePath() throws DviException
+  {
+    if (gsPath == null) {
+      return getDviContext().getExecutableName("gs");
+    }
+    return gsPath;
+  }
+  
+  private int textAlpha = 0;
+  private int graphicsAlpha = 0;
+  
+  private String stdoutData = null;
+  private String stderrData = null;
+  
+  // TODO: externalize ghostscript configurations
+  protected Collection<String> buildCommandLine()
+  throws DviException
+  {
+    ArrayList<String> cl = new ArrayList<String>();
+    String gscmd = getGhostscriptExecutablePath();
+    cl.add(gscmd);
+    cl.add("-q");
+    cl.add("-dSAFER");
+    cl.add("-dPARANOIDSAFER");
+    cl.add("-dDELAYSAFER");
+    cl.add("-dNOPAUSE");
+    cl.add("-dBATCH");
+    cl.add("-sDEVICE=" + dev);
+    if (outputFile != null) {
+      cl.add("-sOutputFile=" + outputFile); // TODO: Escape characters in outputFile.
+    } else {
+      cl.add("-sOutputFile=-"); // the output will be sent to stdout.
+    }
+    if (firstPage > 0) {
+      cl.add("-dFirstPage=" + firstPage);
+    }
+    if (lastPage > 0) {
+      cl.add("-dLastPage=" + lastPage);
+    }
+    cl.add("-r" + dpi);
+    if (size != null)
+      cl.add("-g" + size);
+    if (textAlpha > 0) {
+      cl.add("-dTextAlphaBits=" + textAlpha);
+    }
+    if (graphicsAlpha > 0) {
+      cl.add("-dGraphicsAlphaBits=" + graphicsAlpha);
+    }
+    
+    if (inputFile != null) {
+      cl.add("-f" + inputFile);
+    } else {
+      cl.add("-");
+    }
+    
+    return cl;
+  }
+  
+  public CommandShell createCommandShell() throws DviException
+  {
+    CommandShell cs = new CommandShell();
+    Collection<String> cl = buildCommandLine();
+    cs.setCommandLine(cl);
+    return cs;
+  }
+    
+
+  public void setTextAlpha(int textAlpha)
+  {
+    this.textAlpha = textAlpha;
+  }
+
+  public int getTextAlpha()
+  {
+    return textAlpha;
+  }
+
+  public void setGraphicsAlpha(int graphicsAlpha)
+  {
+    this.graphicsAlpha = graphicsAlpha;
+  }
+
+  public int getGraphicsAlpha()
+  {
+    return graphicsAlpha;
+  }
+
+  public void setFirstPage(int firstPage) {
+    this.firstPage = firstPage;
+  }
+
+  public int getFirstPage() {
+    return firstPage;
+  }
+
+  public void setLastPage(int lastPage) {
+    this.lastPage = lastPage;
+  }
+
+  public int getLastPage() {
+    return lastPage;
+  }
+
+  public void setGhostscriptExecutablePath(String gsPath) {
+    this.gsPath = gsPath;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/gs/GhostscriptUtils.java b/src/jp/sourceforge/dvibrowser/dvicore/gs/GhostscriptUtils.java
new file mode 100644 (file)
index 0000000..778fadc
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.gs;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviPaperSize;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContext;
+import jp.sourceforge.dvibrowser.dvicore.image.pnm.PnmSplitter;
+import jp.sourceforge.dvibrowser.dvicore.image.split.SplitImageWriter;
+import jp.sourceforge.dvibrowser.dvicore.util.BufferFilter;
+import jp.sourceforge.dvibrowser.dvicore.util.CommandShell;
+import jp.sourceforge.dvibrowser.dvicore.util.CommandShellHandler;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+public class GhostscriptUtils {
+  private static final class CommandShellHandlerImpl implements
+      CommandShellHandler {
+    private final ArrayList<String> list;
+    private final String path;
+    public CommandShellHandlerImpl(String path, ArrayList<String> list) {
+      this.list = list;
+      this.path = path;
+    }
+
+    public void handleStderr(InputStream in) throws IOException,
+        DviException {
+      DviUtils.logLinesFromStream("[stderr]", in, LOGGER, Level.WARNING);
+    }
+
+    public void handleStdin(OutputStream out) throws IOException,
+        DviException {
+      out.close();
+    }
+
+    public void handleStdout(InputStream in) throws IOException,
+        DviException {
+      try {
+        Scanner s = new Scanner(in);
+        String line = s.nextLine();
+        if (line.indexOf("Ghostscript") != -1) {
+          list.add(path);
+        }
+        in.close();
+      } finally {
+        DviUtils.silentClose(in);
+      }
+      
+    }
+  }
+
+  private static final Logger LOGGER = Logger.getLogger(GhostscriptUtils.class
+      .getName());
+  
+  public static DviRect [] computePostScriptBoundingBoxes(DviContext ctx, File file, int dpi) throws DviException, IOException, InterruptedException
+  {
+    if (ctx == null)
+      throw new IllegalArgumentException("ctx can't be null");
+    
+    if (file == null)
+      throw new IllegalArgumentException("file can't be null");
+
+    if (dpi <= 0)
+      throw new IllegalArgumentException("dpi <= 0: " + dpi);
+
+    GhostscriptCommandBuilder gs = new GhostscriptCommandBuilder(ctx);
+    gs.setInputFile(file);
+    gs.setResolution(dpi);
+    gs.setDevice("bbox");
+    CommandShell shell = gs.createCommandShell();
+    GhostscriptBBOXParser handler = new GhostscriptBBOXParser();
+    shell.setHandler(handler);
+    shell.execute();
+
+    ArrayList<DviRect> list = new ArrayList<DviRect>();
+
+    LOGGER.info("total pages=" + handler.getTotalPages());
+    for (int i = 0; i < handler.getTotalPages(); i++) {
+      DviRect bbox = handler.getBoundingBoxOfPage(i);
+      LOGGER.info("bounding box[" + i + "]=" + bbox);
+      list.add(bbox);
+    }
+    return list.toArray(new DviRect[list.size()]);
+  }
+   
+  public static void renderAndSplit(final DviContext ctx, final InputStream is,
+      DviSize unitSize, final SplitImageWriter imageWriter,
+      DviResolution res,
+      String deviceName, final int pageNum, final long timeout,
+      final TimeUnit timeUnit) throws DviException {
+    if (deviceName == null) {
+      deviceName = "pnmraw";
+    }
+
+    GhostscriptCommandBuilder gs = new GhostscriptCommandBuilder(ctx);
+    gs.setInputFile(null);
+    gs.setOutputFile(null);
+    gs.setResolution(res.dpi());
+    gs.setDevice(deviceName);
+    gs.setFirstPage(pageNum);
+    gs.setLastPage(pageNum);
+    gs.setTextAlpha(0); // textAlpha and graphicsAlpha have no effects for PNM
+                        // devices.
+    gs.setGraphicsAlpha(0);
+    CommandShell shell = gs.createCommandShell();
+    shell.setTimeout(timeout, timeUnit);
+    final PnmSplitter splitter = new PnmSplitter(unitSize, imageWriter);
+    splitter.setShrinkFactor(res.shrinkFactor());
+    final BufferFilter stdoutFilter = new BufferFilter(16384);
+    CommandShellHandler handler = new CommandShellHandler() {
+      public void handleStderr(InputStream in) throws IOException {
+        try {
+          DviUtils.dumpStreamAsync("gs stderr", in, System.err).join();
+        } catch (InterruptedException e) {
+          DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+        }
+      }
+
+      public void handleStdin(OutputStream out) throws IOException {
+        try {
+          byte[] buf = new byte[16384];
+          BufferedInputStream bis = new BufferedInputStream(is);
+          do {
+            int len = bis.read(buf);
+            if (len < 0)
+              break;
+            out.write(buf, 0, len);
+          } while (true);
+        } finally {
+          DviUtils.silentClose(out);
+          DviUtils.silentClose(is);
+        }
+      }
+
+      public void handleStdout(InputStream in) throws IOException, DviException {
+        stdoutFilter.beginInput(in);
+        try {
+          splitter.splitImageFromStream(stdoutFilter);
+        } finally {
+          try {
+            stdoutFilter.endInput();
+          } catch (Exception e) {
+            DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+          }
+          DviUtils.silentClose(in);
+        }
+      }
+    };
+    shell.setHandler(handler);
+    int ret;
+    
+    try {
+      ret = shell.execute();
+    } catch (IOException e) {
+      LOGGER.warning("Ghostscript failed with output: " + new String(stdoutFilter.getBuffer()));
+      throw new DviException(e);
+    } catch (InterruptedException e) {
+      LOGGER.warning("Ghostscript failed with output: " + new String(stdoutFilter.getBuffer()));
+      throw new DviException(e);
+    }
+    if (ret != 0) {
+      LOGGER.warning("Ghostscript failed with output: " + new String(stdoutFilter.getBuffer()));
+      throw new DviException("Ghostscript failed with exit code " + ret);
+    }
+  }
+  
+  public static DviRect mapToDviCoordinate(DviRect postScriptRect, DviPaperSize paperSize, DviResolution res)
+  {
+    DviRect paperRect = paperSize.toBoundingBox(res);
+    return new DviRect(postScriptRect.x(), paperRect.y() - postScriptRect.y(), postScriptRect.width(), postScriptRect.height());
+  }
+  
+  public static String [] listGhostscriptExecutables()
+  {
+    final ArrayList<String> list = new ArrayList<String>();
+    final ArrayList<String> candidates = new ArrayList<String>();
+    
+    if (DviUtils.isWindows()) {
+      candidates.add("gswin32c");
+    }
+    candidates.add("gs");
+    
+    LOGGER.fine("Start detecting Ghostscript.");
+    for (String path : candidates) {
+      LOGGER.fine("Trying " + path);
+      try {
+        CommandShell cs = new CommandShell();
+        cs.setCommandLine(path, "-dSAFER", "-dPARANOIDSAFER", "-dDELAYSAFER", "-dNOPAUSE", "-dBATCH", "-help");
+        cs.setHandler(new CommandShellHandlerImpl(path, list));
+        cs.execute();
+      } catch (Exception e) {
+        DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      }
+    }
+    LOGGER.fine("Finished detecting Ghostscript.");
+    
+    return list.toArray(new String[list.size()]);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/DefaultDviLayoutManager.java b/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/DefaultDviLayoutManager.java
new file mode 100644 (file)
index 0000000..74537dd
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.gui.swing;
+
+import java.awt.Color;
+import java.awt.GridLayout;
+import java.awt.LayoutManager;
+import java.util.logging.Logger;
+
+import javax.swing.BorderFactory;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviPaperSize;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.ctx.DviToolkit;
+
+
+public class DefaultDviLayoutManager
+extends DviObject
+implements DviLayoutManager
+{
+  private static final Logger LOGGER = Logger.getLogger(DefaultDviLayoutManager.class.getName());
+  private int cols;
+  private int horizontalSpacing;
+  private int verticalSpacing;
+  private int paddingSize;
+  private DviPaperSize paperSize;
+  private boolean crop;
+  private boolean enableDelayedRendering = true;
+  
+  public DefaultDviLayoutManager(DviContextSupport dcs, int cols, int paddingSize)
+  {
+    super(dcs);
+    setNumberOfColumns(cols);
+    setPaddingSize(paddingSize);
+  }
+
+  public DefaultDviLayoutManager(DviContextSupport dcs, int cols)
+  {
+    this(dcs, cols, 0);
+  }
+  
+  private int computeRows(DviDocument doc)
+  throws DviException
+  {
+    int rows = (doc.getTotalPages() + cols - 1)/ cols;
+    return rows;
+  }
+  
+  public LayoutManager createLayout(DviDocument doc, DviResolution res)
+      throws DviException
+  {
+    return new GridLayout(computeRows(doc), cols, horizontalSpacing, verticalSpacing);
+//      return new FlowLayout();
+  }
+  
+  public DviRect getPageBoundingBox(DviDocument doc, DviResolution res)
+  throws DviException
+  {
+    DviToolkit utils = getDviContext().getDviToolkit();
+    if (getEnableCrop()) {
+      DviRect bbox = DviRect.union(utils.computeBoundingBoxes(doc, res));
+      return bbox;
+    } else {
+      DviPaperSize ps = null;
+      try {
+        ps = getDviContext().getDefaultPaperSize();
+      } catch (DviException e) {
+        LOGGER.warning(e.toString());
+      }
+      return utils.computePageBoundingBox(ps, res);
+    }
+  }
+
+  public DviRect getBoundingBox(DviDocument doc, DviResolution res)
+      throws DviException
+  {
+    int rows = computeRows(doc);
+    DviRect bbox = getPageBoundingBox(doc, res);
+    DviRect ret = new DviRect
+      (0, 0, bbox.width() * cols + horizontalSpacing * (cols -1) + paddingSize * (cols + 1) + 16 * (cols),
+          bbox.height() * rows + verticalSpacing * (rows - 1) + paddingSize * (rows + 1) + 16 * (cols));
+    return ret;
+  }
+
+  public int getNumberOfColumns() { return cols; }
+  public void setNumberOfColumns(int cols) {
+    this.cols = Math.max(0, cols);
+  }
+
+  public int getHorizontalSpacing()
+  {
+    return horizontalSpacing;
+  }
+
+  public void setHorizontalSpacing(int horizontalSpacing)
+  {
+    this.horizontalSpacing = horizontalSpacing;
+  }
+
+  public int getVerticalSpacing()
+  {
+    return verticalSpacing;
+  }
+
+  public void setVerticalSpacing(int verticalSpacing)
+  {
+    this.verticalSpacing = verticalSpacing;
+  }
+  
+  public void decoratePage(TDviDocument tdd, TDviPage dp)
+  throws DviException
+  {
+//    DviRect bbox = determinePageSize(dp);
+//    dp.setBoundingBox(bbox);
+    dp.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, new Color(190, 190, 190)));
+  }
+
+  public int getPaddingSize()
+  {
+    return paddingSize;
+  }
+
+  public void setPaddingSize(int paddingSize)
+  {
+    this.paddingSize = paddingSize;
+  }
+
+  public void setPaperSize(DviPaperSize paperSize)
+  {
+    this.paperSize = paperSize;
+  }
+
+  public DviPaperSize getPaperSize()
+  {
+    return paperSize;
+  }
+
+  public void setEnableCrop(boolean crop)
+  {
+    this.crop = crop;
+  }
+
+  public boolean getEnableCrop()
+  {
+    return crop;
+  }
+
+  public boolean getEnableDelayedRendering(TDviPage dp) throws DviException
+  {
+    return enableDelayedRendering;
+  }
+
+  public void setEnableDelayedRendering(boolean enableDelayedRendering)
+  {
+    this.enableDelayedRendering = enableDelayedRendering;
+  }
+
+  public DviRect determinePageSize(TDviPage dp) throws DviException
+  {
+    DviRect bbox = null;
+    DviPage page = dp.getPage();
+    ViewSpec viewSpec = dp.getViewSpec();
+    DviResolution res = viewSpec.getResolution();
+    if (page != null) {
+      bbox = getPageBoundingBox(page.getDocument(), res);
+    }
+    if (bbox == null) {
+      DviToolkit utils = getDviContext().getDviToolkit();
+      bbox = utils.computePageBoundingBox(viewSpec.getPaperSize(), res);
+    }
+    return bbox.addPadding(paddingSize);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/DragToScroll.java b/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/DragToScroll.java
new file mode 100644 (file)
index 0000000..f73ac3e
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.gui.swing;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Cursor;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+
+import javax.swing.JScrollBar;
+import javax.swing.JScrollPane;
+import javax.swing.JViewport;
+import javax.swing.SwingUtilities;
+import javax.swing.event.MouseInputAdapter;
+
+// TODO: support logging
+public class DragToScroll
+extends MouseInputAdapter
+{
+  // Should we make them weak-references?
+  private final JScrollPane jsp;
+       private final JScrollBar hsb, vsb;
+       // TODO: Handle property change by sp.setXXXScrollBar().
+
+  public DragToScroll(JScrollPane jsp) {
+         this.jsp = jsp;
+               hsb = jsp.getHorizontalScrollBar();
+               vsb = jsp.getVerticalScrollBar();
+       }
+
+       public JScrollPane scrollPane() { return jsp; }
+
+       public void add(Component c) {
+         if (c == null) return;
+               addComponent(c);
+               if (c instanceof Container) {
+                 Component [] cs = ((Container) c).getComponents();
+                       for (int i=0; i<cs.length; i++) {
+                   add(cs[i]);
+                       }
+               }
+       }
+
+       private void addComponent(Component c) {
+               c.addMouseMotionListener(this);
+               c.addMouseListener(this);
+       }
+
+       public void remove(Component c) {
+         if (c == null) return;
+               if (c instanceof Container) {
+                 Component [] cs = ((Container) c).getComponents();
+                       for (int i=0; i<cs.length; i++) {
+                   remove(cs[i]);
+                       }
+               }
+               removeComponent(c);
+       }
+
+       private void removeComponent(Component c) {
+         if (c == null) return;
+               c.removeMouseMotionListener(this);
+               c.removeMouseListener(this);
+       }
+
+       public void mouseClicked(MouseEvent e)
+       {
+         debug("mouseClicked");
+       }
+
+       public void mouseEntered(MouseEvent e)
+       {
+         debug("mouseEntered");
+       }
+       public void mouseExited(MouseEvent e)
+       {
+         debug("mouseExited");
+       }
+       private int saveX, saveY;
+       private void savePoint(int x, int y) {
+         saveX = x;
+               saveY = y;
+       }
+
+       public void mousePressed(MouseEvent e)
+       {
+         debug("mousePressed");
+         Cursor cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
+         Component c = (Component) e.getSource();
+         c.setCursor(cursor);
+         
+         if (!e.isPopupTrigger()) {
+                       Point p = e.getPoint();
+                 SwingUtilities.convertPointToScreen(p, e.getComponent());
+                 savePoint(p.x, p.y);
+               }
+       }
+       public void mouseReleased(MouseEvent e)
+       {
+         debug("mouseReleased");
+    Component c = (Component) e.getSource();
+    c.setCursor(null);
+       }
+
+       public void mouseDragged(MouseEvent e)
+       {
+               if (!e.isPopupTrigger()) {
+                 Component c = e.getComponent();
+         debug(e.getSource() + ": mouseDragged");
+                       Point p = e.getPoint();
+                 SwingUtilities.convertPointToScreen(p, c);
+               int dx = p.x - saveX;
+               int dy = p.y - saveY;
+  
+               debug("(dx,dy)=(" + dx + "," + dy + ")");
+  
+      savePoint(p.x, p.y);
+
+                       if (dy == 0) {
+                 hsb.setValue(hsb.getValue() - dx);
+                       } else if (dx == 0) {
+                 vsb.setValue(vsb.getValue() - dy);
+                       } else {
+                   JViewport jv = jsp.getViewport();
+                         jv.scrollRectToVisible(
+                           new Rectangle(-dx, -dy, jv.getWidth(), jv.getHeight())
+                         );
+                       }
+               }
+       }
+
+       public void mouseMoved(MouseEvent e)
+       {
+         debug("mouseMoved");
+       }
+
+       private static void debug(String str) {
+//       System.out.println(str);
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/DviLayoutManager.java b/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/DviLayoutManager.java
new file mode 100644 (file)
index 0000000..aef06e3
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.gui.swing;
+
+import java.awt.LayoutManager;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+
+
+public interface DviLayoutManager
+extends DviContextSupport
+{
+  LayoutManager createLayout(DviDocument doc, DviResolution res) throws DviException;
+  DviRect getBoundingBox(DviDocument doc, DviResolution res) throws DviException;
+  DviRect getPageBoundingBox(DviDocument doc, DviResolution res) throws DviException;
+  boolean getEnableDelayedRendering(TDviPage dp) throws DviException;
+  void decoratePage(TDviDocument tdd, TDviPage dp) throws DviException;
+  DviRect determinePageSize(TDviPage dp) throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TDviDocument.java b/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TDviDocument.java
new file mode 100644 (file)
index 0000000..64b7b66
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.gui.swing;
+
+import java.awt.FlowLayout;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+
+
+public class TDviDocument
+extends JPanel
+implements DviContextSupport, ChangeListener
+{
+  private static final Logger LOGGER = Logger.getLogger(TDviDocument.class.getName());
+       private static final long serialVersionUID = -8289664311728401054L;
+       
+       private JPanel panel = new JPanel();
+
+       private final ArrayList<TDviPage> pageList
+         = new ArrayList<TDviPage>();
+
+       private ViewSpec viewSpec;
+               private DviLayoutManager dviLayout = new DefaultDviLayoutManager(this, 1);
+       
+       public void setViewSpec(ViewSpec vs) throws DviException {
+         if (viewSpec != null) {
+           viewSpec.removeChangeListener(this);
+         }
+         viewSpec = vs;
+    if (viewSpec != null) {
+      viewSpec.addChangeListener(this);
+    }
+    reload();
+       }
+       
+       public ViewSpec getViewSpec() {
+         return viewSpec;
+       }
+       
+       private final DviContextSupport dcs;
+       public DviContext getDviContext() { return dcs.getDviContext(); }
+
+  public TDviDocument(DviContextSupport dcs) {
+    this.dcs = dcs;
+    viewSpec = new ViewSpec(this);
+    viewSpec.setResolution(viewSpec.getResolution().approximate(10));
+    initializeComponents();
+  }
+  
+  private void initializeComponents()
+  {
+//    setBackground(new Color(0xe0, 0xe0, 0xe0));
+    setLayout(new FlowLayout());
+    add(panel);
+  }
+  
+  private boolean enableDelayedRendering = false;
+
+       private DviDocument doc;
+       public void setDviDocument(DviDocument doc) throws DviException
+  {
+    LOGGER.fine("Preparaing TDviDocument with viewSpec=" + viewSpec);
+    this.doc = doc;
+    try {
+      panel.removeAll();
+      pageList.clear();
+      LOGGER.finer("dvi document = " + doc);
+      if (doc != null) {
+        panel.setLayout(getDviLayout().createLayout(doc, viewSpec.getResolution()));
+        for (DviPage page : doc.getPages()) {
+          LOGGER.fine("preparing TDviPage viewSpec=" + viewSpec);
+          TDviPage dp = new TDviPage(this);
+          dp.setViewSpec(viewSpec);
+          dp.setDviLayout(dviLayout);
+          dp.setPage(page);
+          getDviLayout().decoratePage(this, dp);
+          dp.setVisible(true);
+          dp.addChangeListener(new ChangeListener() {
+            public void stateChanged(ChangeEvent e) {
+              TDviDocument.this.fireChangeEvent();
+            }
+          });
+          {
+            MouseListener[] mls = getMouseListeners();
+            for (int j = 0; j < mls.length; j++) {
+              dp.addMouseListener(mls[j]);
+            }
+          }
+          {
+            MouseMotionListener[] mmls = getMouseMotionListeners();
+            for (int j = 0; j < mmls.length; j++) {
+              dp.addMouseMotionListener(mmls[j]);
+            }
+          }
+          panel.add(dp);
+          pageList.add(dp);
+        }
+      } else {
+        LOGGER.finer("dvi document is null");
+      }
+    } catch (Exception ex) {
+      LOGGER.severe(ex.toString());
+    }
+    revalidate();
+    repaint();
+  }
+
+       
+       public void reload()
+       throws DviException
+       {
+         setDviDocument(doc);
+       }
+       
+       public DviDocument getDviDocument() {
+         return doc;
+       }
+
+  public DviLayoutManager getDviLayout()
+  {
+    return dviLayout;
+  }
+
+  public void setDviLayout(DviLayoutManager dviLayout)
+  throws DviException
+  {
+    this.dviLayout = dviLayout;
+    reload();
+  }
+
+  public void setEnableDelayedRendering(boolean enableDelayedRendering)
+  {
+    this.enableDelayedRendering = enableDelayedRendering;
+  }
+
+  public boolean getEnableDelayedRendering()
+  {
+    return enableDelayedRendering;
+  }
+
+  public void stateChanged(ChangeEvent e)
+  {
+    LOGGER.fine("stateChanged: EDT=" + SwingUtilities.isEventDispatchThread() + " viewSpec=" + viewSpec);
+    SwingUtilities.invokeLater(new Runnable() {
+      public void run() {
+        try {
+          reload();
+        } catch (DviException e) {
+          LOGGER.warning(e.toString());
+        }
+      }
+    });
+  }
+  
+  public void addChangeListener(ChangeListener l)
+  {
+      listenerList.add(ChangeListener.class, l);
+  }
+
+  public void removeChangeListener(ChangeListener l)
+  {
+      listenerList.remove(ChangeListener.class, l);
+  }
+
+  protected void fireChangeEvent()
+  {
+    ChangeEvent event = null;
+    
+    Object[] listeners = listenerList.getListenerList();
+    for (int i = listeners.length-2; i>=0; i-=2) {
+      if (listeners[i] == ChangeListener.class) {
+        if (event == null)
+          event = new ChangeEvent(this);
+        ((ChangeListener)listeners[i+1]).stateChanged(event);
+      }
+    }
+  }
+  
+  public boolean isBusy()
+  {
+    for (TDviPage dp : pageList) {
+      if (dp.isBusy()) return true;
+    }
+    return false;
+  }
+
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TDviPage.java b/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TDviPage.java
new file mode 100644 (file)
index 0000000..b4f4804
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.gui.swing;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.Logger;
+
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.ctx.DviToolkit;
+import jp.sourceforge.dvibrowser.dvicore.util.DaemonThreadFactory;
+
+
+public class TDviPage extends JPanel implements DviContextSupport, ChangeListener
+{
+  private static final Logger LOGGER = Logger.getLogger(TDviPage.class.getName());
+  private static final long serialVersionUID = -7167678585159838308L;
+  
+  private boolean busy = false;
+  
+  private final DviContextSupport dcs;
+  public DviContext getDviContext() { return dcs.getDviContext(); }
+
+  public TDviPage(DviContextSupport dcs, DviPage page) {
+    super();
+    this.dcs = dcs;
+    this.viewSpec = new ViewSpec(this);
+    this.viewSpec.setResolution(viewSpec.getResolution().approximate(200));
+    setPage(page);
+  }
+  
+  public TDviPage(DviContextSupport dcs) {
+    this(dcs, null);
+  }
+
+  private DviPage page = null;
+
+  public void setPage(DviPage page)
+  {
+    this.page = page;
+    determineSize();
+  }
+  
+  public DviPage getPage() { return page; }
+
+  private ViewSpec viewSpec;
+
+  public void setViewSpec(ViewSpec viewSpec)
+  {
+    LOGGER.fine("page=" + page + ",viewSpec=" + viewSpec);
+    this.viewSpec = viewSpec;
+    determineSize();
+  }
+  
+  public ViewSpec getViewSpec() { return viewSpec; }
+
+//  private DviRect bbox;
+//
+//  public void setBoundingBox(DviRect bbox)
+//  {
+//    this.bbox = bbox;
+//    determineSize();
+//  }
+//
+//  public DviRect getBoundingBox()
+//  {
+//    return bbox;
+//  }
+
+//  private Anchor.Href myHref = null;
+//  private DviRect saveBBOX = null;
+//
+//  public ByteRange getByteRangeFor(DviRect rect)
+//  {
+//    try {
+//      DviResolution res = viewSpec.getResolution();
+//
+//      rect = rect.intersect(displayRect);
+//      if (rect.isEmpty())
+//        return null;
+//      rect = rect.magnify(res.shrinkFactor());
+//
+//      Geometer geometer = new BasicGeometer(this);
+//      ByteRangeComputer brc = new ByteRangeComputer(this, res, rect);
+//      geometer.setPainter(brc);
+//      getDviContext().execute(page, geometer);
+//      AnchorSet anchors = (AnchorSet) rsc.getDataSource().find(AnchorSet.class,
+//          page);
+//      if (myHref != null) {
+//        anchors.remove(myHref);
+//      }
+//      ByteRange br = brc.getByteRange();
+//      anchors.add(myHref = new Anchor.Href(br.begin(), br.end(), "none"));
+//      if (saveBBOX != null) {
+//        DviRect r = saveBBOX;
+//        repaint(r.x(), r.y(), r.width(), r.height());
+//      }
+//      DviRect r = brc.getBounds().shrink(res.shrinkFactor());
+//      repaint(r.x(), r.y(), r.width(), r.height());
+//      saveBBOX = r;
+//      return br;
+//    } catch (DviException ex) {
+//      Logger.trace(ex);
+//      return null;
+//    }
+//  }
+
+  private DviRect displayRect = null;
+
+  public void determineSize()
+  {
+    final DviToolkit utils = getDviContext().getDviToolkit();
+    displayRect = null;
+    try {
+      if (layout != null)
+        displayRect = layout.determinePageSize(this);
+    } catch (Exception e) {
+      LOGGER.severe(e.toString());
+      e.printStackTrace();
+    }
+    if (displayRect == null) {
+      displayRect = utils.computePageBoundingBox
+        (viewSpec.getPaperSize(), viewSpec.getResolution());
+    }
+
+    setPreferredSize(new Dimension(displayRect.width(), displayRect.height()));
+    setSize(new Dimension(displayRect.width(), displayRect.height()));
+    revalidate();
+  }
+  
+  private static ExecutorService exe = Executors.newFixedThreadPool(3, new DaemonThreadFactory());
+  
+  protected ExecutorService getExecutorService()
+  {
+    return exe;
+  }
+  
+  protected void startPagePreparation(Graphics g, final Rectangle rect, final DviPage page, final ViewSpec viewSpec)
+  {
+    final DviToolkit utils = getDviContext().getDviToolkit();
+
+    LOGGER.finer("Preparing page for paint: " + page);
+    getExecutorService().submit(new Runnable() {
+      public void run()
+      {
+        try {
+          utils.prepareForRendering(page, viewSpec);
+        } catch (Exception e) {
+          LOGGER.warning(e.toString());
+        }
+        SwingUtilities.invokeLater(new Runnable() {
+          public void run()
+          {
+            setBusy(false);
+            repaint();
+          }
+        });
+      }
+    });
+    setBusy(true);
+    paintDummyContents(g, rect, viewSpec);
+  }
+
+  private void paintDummyContents(Graphics g, final Rectangle rect,
+      final ViewSpec viewSpec)
+  {
+    g.setColor(viewSpec.getBackgroundColor().toColor());
+    g.fillRect(rect.x, rect.y, rect.width, rect.height);
+  }
+  
+  protected void paintComponent(Graphics g)
+  {
+    super.paintComponent(g);
+
+    final DviToolkit utils = getDviContext().getDviToolkit();
+    
+    if (page == null) return;
+    
+    final Rectangle rect = g.getClipBounds();
+    g.fillRect(rect.x, rect.y, rect.width, rect.height);
+
+    boolean enableDelayedRendering = true;
+    
+    try {
+      enableDelayedRendering = getEnableDelayedRendering();
+    } catch (DviException e1) {
+      LOGGER.warning(e1.toString());
+    }
+    
+    if (enableDelayedRendering) {
+      if (!utils.canRenderPageImmediately(page, viewSpec)) {
+        LOGGER.finest("delayed rendering");
+        startPagePreparation(g, rect, page, viewSpec);
+        return;
+      }
+    }
+    
+    try {
+      LOGGER.finest("rendering page=" + page.getPageNumber() + " rect=" + rect + " viewSpec=" + viewSpec);
+//      DviRect targetArea = new DviRect
+//        (rect.x + displayRect.x(), rect.y + displayRect.y(), rect.width+1, rect.height+1);
+      DviRect targetArea = DviRect.fromRectangle(rect).translate(displayRect.topLeft());
+      if (!targetArea.isEmpty()) {
+          BufferedImage img = utils.renderToBufferedImage(page, targetArea, viewSpec);
+          g.drawImage(img, rect.x, rect.y, null);
+      }
+    } catch (DviException e) {
+      LOGGER.warning(e.toString());
+      e.printStackTrace();
+      paintDummyContents(g, rect, viewSpec);
+    }
+  }
+  
+  public void stateChanged(ChangeEvent e)
+  {
+    LOGGER.fine("stateChanged called: EDT=" + SwingUtilities.isEventDispatchThread());
+    SwingUtilities.invokeLater(new Runnable() {
+      public void run()
+      {
+        determineSize();
+        revalidate();
+        repaint();
+      }
+    });
+  }
+  
+  private DviLayoutManager layout = new DefaultDviLayoutManager(this, 1);
+  
+  public boolean getEnableDelayedRendering()
+  throws DviException
+  {
+    if (layout == null) return true;
+    return layout.getEnableDelayedRendering(this);
+  }
+
+  public DviLayoutManager getDviLayout()
+  {
+    return layout;
+  }
+
+  public void setDviLayout(DviLayoutManager layout)
+  {
+    if (layout == null)
+      throw new NullPointerException("layout");
+    this.layout = layout;
+  }
+
+  protected void setBusy(boolean busy) {
+    this.busy = busy;
+    fireChangeEvent();
+  }
+
+  public boolean isBusy() {
+    return busy;
+  }
+  
+  protected EventListenerList listenerList = new EventListenerList();
+
+  public void addChangeListener(ChangeListener l)
+  {
+      listenerList.add(ChangeListener.class, l);
+  }
+
+  public void removeChangeListener(ChangeListener l)
+  {
+      listenerList.remove(ChangeListener.class, l);
+  }
+
+  protected void fireChangeEvent()
+  {
+    ChangeEvent event = null;
+    
+    Object[] listeners = listenerList.getListenerList();
+    for (int i = listeners.length-2; i>=0; i-=2) {
+      if (listeners[i] == ChangeListener.class) {
+        if (event == null)
+          event = new ChangeEvent(this);
+        ((ChangeListener)listeners[i+1]).stateChanged(event);
+      }
+    }
+  }
+
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TScrollPane.java b/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TScrollPane.java
new file mode 100644 (file)
index 0000000..399a432
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.gui.swing;
+
+import java.awt.Component;
+import java.awt.Graphics;
+
+import javax.swing.JScrollPane;
+
+// Used in dvibrowser to minimize paints.
+public class TScrollPane extends JScrollPane {
+       private static final long serialVersionUID = -2913809707498868152L;
+
+       public TScrollPane() {
+               super();
+       }
+
+       public TScrollPane(Component view) {
+               super(view);
+       }
+
+       private boolean updating = false;
+
+       public void beginUpdate() {
+               updating = true;
+       }
+
+       public void endUpdate() {
+               updating = false;
+               repaint();
+       }
+
+       public void paintComponent(Graphics g) {
+               if (!updating) {
+                       super.paintComponent(g);
+               }
+       }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TexLogViewer.java b/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/TexLogViewer.java
new file mode 100644 (file)
index 0000000..5b0089c
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.gui.swing;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Scanner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+public class TexLogViewer extends JPanel {
+  private static final long serialVersionUID = 7630772409309800485L;
+  private final JScrollPane scroll;
+  private final JTextArea textArea;
+  private boolean hasError = false;
+  
+  private File dviFile;
+  public TexLogViewer(int rows, int cols) {
+    setDviFile(null);
+    textArea = new JTextArea(rows, cols);
+    textArea.setOpaque(true);
+    textArea.setEditable(false);
+    scroll = new JScrollPane(textArea);
+    setLayout(new BorderLayout());
+    add(scroll, BorderLayout.CENTER);
+  }
+  
+  public File getDviFile() { return dviFile; }
+  public void setDviFile(File dviFile) {
+    this.dviFile = dviFile;
+    setupView();
+  }
+  
+  private File getLogFile() {
+    String filename = dviFile.getPath();
+    String filenameBase = filename.replaceFirst("\\.[_a-zA-Z0-9]*$", "");
+    File logFile = new File(filenameBase + ".log");
+    return logFile;
+  }
+  
+  private void setupView() {
+    hasError = false;
+    if (dviFile == null) return;
+
+    File logFile = getLogFile();
+    boolean success = true;
+    if (logFile.exists()) {
+      StringBuilder sb = new StringBuilder();
+      String extraInfo = "";
+      try {
+        Scanner s = new Scanner(logFile);
+        while (s.hasNextLine()) {
+          String line = s.nextLine();
+          sb.append(line);
+          if (0 == line.indexOf("! Emergency stop.")) {
+            success = false;
+            Pattern pat = Pattern.compile("^l.([0-9]*) (.*)$");
+            while (s.hasNextLine()) {
+              line = s.nextLine();
+              sb.append(line);
+              Matcher mat = pat.matcher(line);
+              if (mat.matches()) {
+                extraInfo = line;
+                break;
+              }
+            }
+          }
+          sb.append("\n");
+        }
+        s.close();
+        sb.append("---[FOUND ERRORS]---\n");
+        sb.append(extraInfo + "\n");
+        textArea.setText(sb.toString());
+      } catch (FileNotFoundException e) {
+        e.printStackTrace();
+      }
+    }
+    if (success) {
+      textArea.setBackground(null);
+    } else {
+      textArea.setBackground(new Color(0xff, 0xcc, 0x60));
+    }
+    hasError = !success;
+  }
+  
+  public boolean hasError() { return hasError; }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/ViewSpec.java b/src/jp/sourceforge/dvibrowser/dvicore/gui/swing/ViewSpec.java
new file mode 100644 (file)
index 0000000..3772fe8
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.gui.swing;
+
+// mutable.
+// TODO: make this class independent of Swing and move it to another package.
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+import jp.sourceforge.dvibrowser.dvicore.DviColor;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviPaperSize;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.GammaCorrector;
+import jp.sourceforge.dvibrowser.dvicore.render.DefaultGammaCorrector;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+public class ViewSpec
+extends DviObject
+implements Cloneable
+{
+  private static final Logger LOGGER = Logger.getLogger(ViewSpec.class.getName());
+  
+  protected EventListenerList listenerList = new EventListenerList();
+  
+  public void addChangeListener(ChangeListener l)
+  {
+      listenerList.add(ChangeListener.class, l);
+  }
+
+  public void removeChangeListener(ChangeListener l)
+  {
+      listenerList.remove(ChangeListener.class, l);
+  }
+
+  protected void fireChangeEvent()
+  {
+    ChangeEvent event = null;
+    
+    Object[] listeners = listenerList.getListenerList();
+    for (int i = listeners.length-2; i>=0; i-=2) {
+      if (listeners[i] == ChangeListener.class) {
+        if (event == null)
+          event = new ChangeEvent(this);
+        ((ChangeListener)listeners[i+1]).stateChanged(event);
+      }
+    }
+  }
+  
+  // TODO: outsource configurations to a config file.
+  private DviColor backgroundColor = new DviColor(255, 255, 255);
+  private int epsResolutionDpi = 200;
+  private String epsImageFileExtension = ".jpg";
+  private String epsImageDeviceName = "jpeg"; // passed to Ghostscript
+  
+  private DviColor foregroundColor = new DviColor(0, 0, 0);
+  private static final GammaCorrector defaultGammaCorrector = new DefaultGammaCorrector(1.4, 1.2);
+  private GammaCorrector gammaCorrector = defaultGammaCorrector;
+
+  private DviPaperSize paperSize;
+  
+  private boolean postScriptEnabled = true;
+  
+  private DviResolution res;
+  
+  public ViewSpec(DviContextSupport dcs) {
+    super(dcs);
+    try {
+      res = getDviContext().getDefaultResolution();
+    } catch (DviException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+    }
+    if (res == null) {
+      res = new DviResolution(2400, 20);
+    }
+  }
+
+  // Note that we don't copy the listeners.
+  public Object clone()
+ {
+    try {
+      ViewSpec vs = (ViewSpec) super.clone();
+      vs.backgroundColor = this.backgroundColor;
+      vs.epsResolutionDpi = this.epsResolutionDpi;
+      vs.foregroundColor = this.foregroundColor;
+      vs.gammaCorrector = this.gammaCorrector;
+      vs.paperSize = this.paperSize;
+      vs.postScriptEnabled = this.postScriptEnabled;
+      vs.res = this.res;
+      vs.epsImageDeviceName = this.epsImageDeviceName;
+      vs.epsImageFileExtension = this.epsImageFileExtension;
+      vs.epsResolutionDpi = this.epsResolutionDpi;
+      return vs;
+    } catch (CloneNotSupportedException e) {
+      throw new InternalError();
+    }
+  }
+
+  public DviColor getBackgroundColor()
+  {
+    return backgroundColor;
+  }
+
+  public int getEpsResolutionDpi()
+  {
+    return epsResolutionDpi;
+  }
+  
+  public DviColor getForegroundColor()
+  {
+    return foregroundColor;
+  }
+
+  public GammaCorrector getGammaCorrector()
+  {
+    return gammaCorrector;
+  }
+  public DviPaperSize getPaperSize()
+  {
+    return paperSize;
+  }
+  public DviResolution getResolution()
+  {
+    return res;
+  }
+
+  public boolean isPostScriptEnabled()
+  {
+    return postScriptEnabled;
+  }
+  public void setPostScriptEnabled(boolean postScriptEnabled)
+  {
+    this.postScriptEnabled = postScriptEnabled;
+    fireChangeEvent();
+  }
+  
+  public void setBackgroundColor(DviColor c)
+  {
+    backgroundColor = c;
+    fireChangeEvent();
+  }
+  
+  public void setEpsResolutionDpi(int epsResolutionDpi)
+  {
+    this.epsResolutionDpi = epsResolutionDpi;
+    fireChangeEvent();
+  }
+  
+  public void setForegroundColor(DviColor c)
+  {
+    foregroundColor = c;
+    fireChangeEvent();
+  }
+
+  public void setGammaCorrector(GammaCorrector gc)
+  {
+    this.gammaCorrector = gc;
+    fireChangeEvent();
+  }
+
+  public void setPaperSize(DviPaperSize paperSize)
+  {
+    this.paperSize = paperSize;
+    fireChangeEvent();
+  }
+
+  public void setResolution(DviResolution res)
+  {
+    this.res = res;
+    LOGGER.finer("res=" + res);
+    fireChangeEvent();
+  }
+
+  public void setEpsImageFileExtension(String epsImageFileExtension)
+  {
+    this.epsImageFileExtension = epsImageFileExtension;
+    fireChangeEvent();
+  }
+
+  public String getEpsImageFileExtension()
+  {
+    return epsImageFileExtension;
+  }
+
+  public void setEpsImageDeviceName(String epsImageDevice)
+  {
+    this.epsImageDeviceName = epsImageDevice;
+    fireChangeEvent();
+  }
+
+  public String getEpsImageDeviceName()
+  {
+    return epsImageDeviceName;
+  }
+  
+  public String toString()
+  {
+    return getClass().getName()
+      + "[backgroundColor=" + this.backgroundColor
+      + ",epsResolutionDpi=" + this.epsResolutionDpi
+      + ",foregroundColor=" + this.foregroundColor
+      + ",gammaCorrector=" + this.gammaCorrector
+      + ",paperSize=" + this.paperSize
+      + ",postScriptEnabled=" + this.postScriptEnabled
+      + ",resolution=" + this.res
+      + "]"
+      ;
+  }
+
+  public void setApproximateDpi(double dpi) {
+    setResolution(res.approximate(dpi));
+  }
+
+  public static GammaCorrector getDefaultGammaCorrector() {
+    return defaultGammaCorrector;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/AbstractPnmAsciiFilter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/AbstractPnmAsciiFilter.java
new file mode 100644 (file)
index 0000000..6262601
--- /dev/null
@@ -0,0 +1,110 @@
+package jp.sourceforge.dvibrowser.dvicore.image.pnm;
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public abstract class AbstractPnmAsciiFilter
+extends FilterInputStream
+{
+  protected BufferedReader reader;
+  protected ByteArrayInputStream byteArrayInput;
+  
+  public AbstractPnmAsciiFilter(InputStream in) {
+    super(in);
+    reader = new BufferedReader(new InputStreamReader(in));
+  }
+  
+  protected synchronized void fill() throws IOException
+  {
+    if (reader == null) {
+      throw new IOException("EOF detected while reading the input");
+    }
+    if (byteArrayInput != null && byteArrayInput.available() > 0) {
+      return;
+    }
+    
+    byteArrayInput = new ByteArrayInputStream(fillBuffer());
+  }
+
+  protected abstract byte [] fillBuffer() throws IOException;
+  
+  @Override
+  public synchronized int available() throws IOException
+  {
+    fill();
+    return byteArrayInput.available();
+  }
+  
+  @Override
+  public synchronized void close() throws IOException
+  {
+    reader.close();
+  }
+  
+  @Override
+  public void mark(int readLimit)
+  {
+    throw new UnsupportedOperationException("mark is not supported");
+  }
+  
+  @Override
+  public void reset()
+  {
+    throw new UnsupportedOperationException("reset is not supported");
+  }
+  
+  @Override
+  public boolean markSupported()
+  {
+    return false;
+  }
+  
+  @Override
+  public synchronized int read() throws IOException
+  {
+    fill();
+    return byteArrayInput.read();
+  }
+
+  @Override
+  public synchronized int read(byte [] buf, int off, int len) throws IOException
+  {
+    fill();
+    return byteArrayInput.read(buf, off, len);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/AbstractPnmSplitter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/AbstractPnmSplitter.java
new file mode 100644 (file)
index 0000000..31a1187
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+package jp.sourceforge.dvibrowser.dvicore.image.pnm;
+
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+import jp.sourceforge.dvibrowser.dvicore.image.split.ImageSplitter;
+import jp.sourceforge.dvibrowser.dvicore.image.split.SplitImageWriter;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+public abstract class AbstractPnmSplitter
+implements ImageSplitter
+{
+  private static final Logger LOGGER = Logger.getLogger(AbstractPnmSplitter.class
+      .getName());
+  protected final SplitImageWriter imageWriter;
+  protected DataInputStream input;
+  protected PnmHeader header;
+  private final DviSize unitSize;
+  private int shrinkFactor = 1;
+  
+  public AbstractPnmSplitter(DviSize unitSize, SplitImageWriter imageWriter)
+  {
+    this.unitSize = unitSize;
+    if (imageWriter == null)
+      throw new IllegalArgumentException("imageWriter can't be null");
+    
+    this.imageWriter = imageWriter;
+    header = null;
+  }
+  
+  public PnmHeader getPnmHeader()
+  {
+    return header;
+  }
+  
+  
+  protected void beginSplitInternal(PnmHeader header, InputStream is)
+  throws DviException
+  {
+    this.header = header;
+    LOGGER.fine("PnmHeader: " + header);
+    input = new DataInputStream(is);
+  }
+  
+  protected void endSplitInternal()
+  throws DviException
+  {
+  }
+  
+  protected abstract byte [] createLineBuffer();
+  
+  protected abstract BufferedImage createBufferedImage(DviRect box);
+  
+  protected abstract int copyLineToDataBuffer(byte [] buf, int x, DviRect box, int i, DataBufferByte data);
+  
+  protected void readFully(byte [] buf) throws IOException
+  {
+    input.readFully(buf);
+  }
+  
+  protected DviRectSplitter createRectSplitter(DviRect rect, DviSize unitSize)
+  {
+    return new DviRectSplitter(rect, unitSize);
+  }
+  
+  public void splitImageFromStream(InputStream is)
+  throws IOException, DviException
+  {
+    try {
+      PnmHeader header = PnmHeader.parseHeader(is);
+      beginSplitInternal(header, is);
+      try {
+        DviRect rect = new DviRect(0, 0, header.getWidth(), header.getHeight());
+        final int sf = shrinkFactor;
+        final DviSize samplingSize = unitSize.magnify(sf);
+        DviRectSplitter splitter = createRectSplitter(rect.shrink(sf), unitSize);
+        DviRectSplitter samplingSplitter = createRectSplitter(rect, samplingSize);
+        
+        DviRect [][] rects = splitter.getRects();
+        DviRect [][] samplingRects = samplingSplitter.getRects();
+        int rows = splitter.getNumRows();
+        int cols = splitter.getNumColumns();
+        
+        imageWriter.beginSplitImage(this, splitter);
+        
+        try {
+          byte [] buf = createLineBuffer();
+          for (int row=0; row<rows; row++) {
+            final BufferedImage [] imgs = new BufferedImage[cols];
+            final DataBufferByte [] buffers = new DataBufferByte[cols];
+            int h = 0;
+            LOGGER.fine("Reading row=" + row + "/" + rows);
+            for (int col =0; col < cols; col++) {
+              final DviRect box = samplingRects[row][col];
+              imgs[col] = createBufferedImage(box);
+              h = box.height();
+              buffers[col] = (DataBufferByte) imgs[col].getRaster().getDataBuffer();
+            }
+            for (int i=0; i<h; i++) {
+              readFully(buf);
+              int x = 0;
+              for (int col =0; col < cols; col++) {
+                final DviRect box = samplingRects[row][col];
+                final int uw = copyLineToDataBuffer(buf, x, box, i, buffers[col]);
+                x += uw;
+              }
+            }
+            for (int col = 0; col < cols; col++) {
+              if (sf == 1) {
+                final BufferedImage img = imgs[col];
+                imageWriter.writeImagePiece(img, row, col);
+              } else {
+                final DviRect box = rects[row][col];
+                final BufferedImage img = imgs[col];
+                final BufferedImage img2 = new BufferedImage(box.width(), box.height(), BufferedImage.TYPE_INT_RGB);
+                Graphics2D g = img2.createGraphics();
+                g.drawImage(img.getScaledInstance(box.width(), box.height(), Image.SCALE_AREA_AVERAGING), 0, 0, null);
+                g.dispose();
+                imageWriter.writeImagePiece(img2, row, col);
+              }
+            }
+          }
+        } finally {
+          imageWriter.endSplitImage();
+        }
+      } finally {
+        endSplitInternal();
+      }
+    } finally {
+      LOGGER.fine("Closing the stream");
+      DviUtils.silentClose(input);
+    }
+  }
+  
+  public SplitImageWriter getSplitImageWriter() {
+    return imageWriter;
+  }
+
+  public DviSize getUnitSize() {
+    return unitSize;
+  }
+
+  public void setShrinkFactor(int shrinkFactor) {
+    if (0 >= shrinkFactor)
+      throw new IllegalArgumentException("shrinkFactor is non-positive.");
+    this.shrinkFactor = shrinkFactor;
+  }
+
+  public int getShrinkFactor() {
+    return shrinkFactor;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PbmSplitter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PbmSplitter.java
new file mode 100644 (file)
index 0000000..7a608ac
--- /dev/null
@@ -0,0 +1,73 @@
+package jp.sourceforge.dvibrowser.dvicore.image.pnm;
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.awt.image.IndexColorModel;
+
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+import jp.sourceforge.dvibrowser.dvicore.image.split.SplitImageWriter;
+
+public class PbmSplitter
+extends AbstractPnmSplitter
+{
+//  private static final Logger LOGGER = Logger.getLogger(PbmSplitter.class
+//      .getName());
+  public PbmSplitter(DviSize unitSize, SplitImageWriter imageWriter)
+  {
+    super(unitSize, imageWriter);
+  }
+  
+  @Override
+  protected byte [] createLineBuffer()
+  {
+    return new byte [(header.getWidth() + 7) / 8];
+  }
+  
+  @Override
+  protected BufferedImage createBufferedImage(DviRect box)
+  {
+    final byte[] colors = {(byte)255, 0};
+    final IndexColorModel cm = new IndexColorModel(1,2,colors,colors,colors);
+    return new BufferedImage(box.width(), box.height(), BufferedImage.TYPE_BYTE_BINARY, cm);
+  }
+  
+  @Override
+  protected int copyLineToDataBuffer(byte [] buf, int x, DviRect box, int i, DataBufferByte data)
+  {
+    final int uw = (box.width() + 7) /8;
+    System.arraycopy(buf, x, data.getData(), i * uw, uw);
+    return uw;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PgmSplitter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PgmSplitter.java
new file mode 100644 (file)
index 0000000..a23ecf6
--- /dev/null
@@ -0,0 +1,95 @@
+package jp.sourceforge.dvibrowser.dvicore.image.pnm;
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.io.InputStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+import jp.sourceforge.dvibrowser.dvicore.image.split.SplitImageWriter;
+
+public class PgmSplitter
+extends AbstractPnmSplitter
+{
+//  private static final Logger LOGGER = Logger.getLogger(PgmSplitter.class
+//      .getName());
+  public PgmSplitter(DviSize unitSize, SplitImageWriter imageWriter)
+  {
+    super(unitSize, imageWriter);
+  }
+  
+  private final int [] table = new int[256];
+  
+  @Override
+  protected void beginSplitInternal(PnmHeader header, InputStream is) throws DviException
+  {
+    super.beginSplitInternal(header, is);
+    int maxval = header.getMaxValue();
+    for (int i=0; i<256; i++) {
+      table[i] = Math.max(Math.min(i * maxval / 255, maxval), 0);
+    }
+  }
+  
+  @Override
+  protected byte [] createLineBuffer()
+  {
+    return new byte [header.getWidth()];
+  }
+  
+  @Override
+  protected BufferedImage createBufferedImage(DviRect box)
+  {
+    //TODO: use color model.
+    BufferedImage bi = new BufferedImage(box.width(), box.height(), BufferedImage.TYPE_BYTE_GRAY);
+    return bi;
+  }
+  
+  @Override
+  protected int copyLineToDataBuffer(final byte [] buf, final int x, final DviRect box, final int i, final DataBufferByte data)
+  {
+    final int w = box.width();
+    final int uw = w;
+    final byte [] dst = data.getData();
+    for (int j=0; j<w; j++) {
+      dst[j + i * uw] = (byte) table[byteToInt(buf[x + j])];
+    }
+    return uw;
+  }
+
+  private int byteToInt(byte b) {
+    if (b >= 0) return b;
+    return 256 + b;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmBitAsciiFilter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmBitAsciiFilter.java
new file mode 100644 (file)
index 0000000..c0e6f3c
--- /dev/null
@@ -0,0 +1,73 @@
+package jp.sourceforge.dvibrowser.dvicore.image.pnm;
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+public class PnmBitAsciiFilter
+extends AbstractPnmAsciiFilter
+{
+  public PnmBitAsciiFilter(InputStream in) {
+    super(in);
+  }
+  
+  protected synchronized byte [] fillBuffer() throws IOException
+  {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    String line = reader.readLine();
+    if (line == null) {
+      throw new IOException("EOF detected while reading the input");
+    }
+    while (line.length() > 0) {
+      String d = null;
+      if (line.length() >= 8) {
+        d = line.substring(0, 8);
+        line = line.substring(8);
+      } else {
+        d = (line + "0000000").substring(0, 8);
+        line = "";
+      }
+      try {
+        baos.write(Integer.parseInt(d, 2));
+      } catch (NumberFormatException e) {
+        throw new IOException("Input format error: data is not binary: " + line);
+      }
+    }
+    if (baos.size() == 0) {
+      throw new IOException("EOF detected while reading the input");
+    }
+    return baos.toByteArray();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmByteAsciiFilter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmByteAsciiFilter.java
new file mode 100644 (file)
index 0000000..bceb63d
--- /dev/null
@@ -0,0 +1,72 @@
+package jp.sourceforge.dvibrowser.dvicore.image.pnm;
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.StringTokenizer;
+
+
+public class PnmByteAsciiFilter
+extends AbstractPnmAsciiFilter
+{
+  private static final int BUFFER_SIZE = 1024;
+  private static final String DELIMS = " \t\r\n";
+  
+  public PnmByteAsciiFilter(InputStream in) {
+    super(in);
+  }
+  
+  protected synchronized byte [] fillBuffer() throws IOException
+  {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    String line = reader.readLine();
+    if (line == null) {
+      throw new IOException("EOF detected while reading the input");
+    }
+    StringTokenizer st = new StringTokenizer(line, DELIMS);
+    while (st.hasMoreTokens() && baos.size() < BUFFER_SIZE) {
+      String hex = st.nextToken();
+      try {
+        int value = Integer.parseInt(hex, 10);
+        baos.write(value);
+      } catch (NumberFormatException e) {
+        throw new IOException("Input value is not a hexadecimal: " + hex);
+      }
+    }
+    if (baos.size() == 0) {
+      throw new IOException("EOF detected while reading the input");
+    }
+    return baos.toByteArray();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmHeader.java b/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmHeader.java
new file mode 100644 (file)
index 0000000..226aae2
--- /dev/null
@@ -0,0 +1,241 @@
+package jp.sourceforge.dvibrowser.dvicore.image.pnm;
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+
+public final class PnmHeader
+{
+  private static final Logger LOGGER = Logger.getLogger(PnmHeader.class
+      .getName());
+  
+  public static final String PNM_COMMENT_NEWLINE = "\n";
+  
+  public static final int PNM_TYPE_BITMAP_ASCII = 1;
+  public static final int PNM_TYPE_GRAYMAP_ASCII = 2;
+  public static final int PNM_TYPE_PIXMAP_ASCII = 3;
+  public static final int PNM_TYPE_BITMAP_BINARY = 4;
+  public static final int PNM_TYPE_GRAYMAP_BINARY = 5;
+  public static final int PNM_TYPE_PIXMAP_BINARY = 6;
+
+  private final int type;
+  private final int width;
+  private final int height;
+  private final int maxval;
+  private final String comment;
+  
+  public PnmHeader(int type, String comment, int width, int height, int maxval)
+  {
+    this.type = type;
+    this.comment = comment;
+    this.width = width;
+    this.height = height;
+    this.maxval = maxval;
+  }
+  
+  public boolean isBitmap()
+  {
+    return (PNM_TYPE_BITMAP_ASCII == type || PNM_TYPE_BITMAP_BINARY == type);
+  }
+  
+  public boolean isGraymap()
+  {
+    return (PNM_TYPE_GRAYMAP_ASCII == type || PNM_TYPE_GRAYMAP_BINARY == type);
+  }
+
+  public boolean isPixmap()
+  {
+    return (PNM_TYPE_PIXMAP_ASCII == type || PNM_TYPE_PIXMAP_BINARY == type);
+  }
+  
+  public boolean isASCII()
+  {
+    return (1 <= type && type <= 3);
+  }
+
+  public boolean isBinary()
+  {
+    return !isASCII();
+  }
+  
+  public int getType() {
+    return type;
+  }
+
+  public int getWidth() {
+    return width;
+  }
+
+  public int getHeight() {
+    return height;
+  }
+
+  public int getMaxValue() {
+    return maxval;
+  }
+  
+  public String toString() {
+    return getClass().getName() + "[type=" + type + ",width=" + width
+        + ",height=" + height + ",maxval=" + maxval + ",comment=" + comment + "]";
+  }
+  
+  // P1: bitmap (ASCII)
+  // P2: graymap (ASCII)
+  // P3: pixmap (ASCII)
+  // P4: bitmap (binary)
+  // P5: graymap (binary)
+  // P6: pixmap (binary)
+  
+//  private static final Pattern patPnmMagicNumbers = Pattern.compile("P([1-6])");
+  private static final Pattern patPnmComment = Pattern.compile("#\\s?(.*)");
+  private static final Pattern patPnmSize = Pattern.compile("([0-9][0-9]*)\\s\\s*([0-9][0-9]*)");
+  
+  public String readLine(InputStream is)
+  throws IOException
+  {
+    StringBuilder sb = new StringBuilder();
+
+    int c = -1;
+    do {
+      c = is.read();
+    } while (c != '\n');
+    
+    return sb.toString();
+  }
+  
+  public static String readLine(InputStream is, int c, int delim)
+  throws IOException
+  {
+    String line = "";
+    do {
+      if (c >= 0) {
+        line += (char) c;
+      }
+      c = is.read();
+      if (c < 0)
+        return null;
+    } while (c != delim);
+    return line;
+  }
+
+  
+  public static PnmHeader parseHeader(InputStream is)
+  throws IOException
+  {
+    int type=-1, width=-1, height=-1, maxval=-1;
+    String comment = null;
+    
+    {
+      int c1 = is.read();
+      int c2 = is.read();
+    
+      if (!(c1 == 'P' && '1' <= c2 && c2 <= '6'))
+        throw new IOException("Invalid PNM magic number: c1=" + (char) c1 + " c2=" + (char) c2);
+      type = c2 - '0';
+    }
+    
+    int c = -1;
+    int delim = -1;
+    
+    c = is.read();
+    
+    while (isNewLine(c)) {
+      delim = c;
+      c = is.read();
+    }
+    
+    if (delim < 0) {
+      throw new IOException("Illegal data after PNM magic number: " + c);
+    }
+    
+    // Parse Comments.
+    String line = readLine(is, c, delim);
+    while (null != line) {
+      Matcher mat = patPnmComment.matcher(line);
+      if (!mat.matches()) break;
+      String s = mat.group(1);
+      comment = (comment == null) ? s : comment + PNM_COMMENT_NEWLINE + s;
+      line = readLine(is, -1, delim);
+    }
+
+    // Parse size.
+    {
+      Matcher mat2 = patPnmSize.matcher(line);
+      if (mat2.matches()) {
+        try {
+          width = Integer.parseInt(mat2.group(1));
+          height = Integer.parseInt(mat2.group(2));
+        } catch (NumberFormatException e) {
+          DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+        }
+      }
+    }
+    if (width < 0 || height < 0) {
+      throw new IOException("Failed to decode PNM image size: " + line);
+    }
+    
+    // Parse max value (which is required for graymap and pixmap).
+    if (PNM_TYPE_BITMAP_ASCII == type || PNM_TYPE_BITMAP_BINARY == type) {
+      maxval = 1;
+    } else {
+      line = readLine(is, -1, delim);
+      try {
+        maxval = Integer.parseInt(line);
+      } catch (NumberFormatException e) {
+        DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      }
+      if (maxval < 0) {
+        throw new IOException("Failed to decode PNM max value: " + line);
+      }
+    }
+    
+    return new PnmHeader(type, comment, width, height, maxval);
+  }
+
+  private static boolean isNewLine(int c) {
+    return (c == '\r' || c == '\n');
+  }
+
+  public String getComment() {
+    return comment;
+  }
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmSplitter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PnmSplitter.java
new file mode 100644 (file)
index 0000000..446dee2
--- /dev/null
@@ -0,0 +1,118 @@
+package jp.sourceforge.dvibrowser.dvicore.image.pnm;
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.io.IOException;
+import java.io.InputStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+import jp.sourceforge.dvibrowser.dvicore.image.split.SplitImageWriter;
+
+public class PnmSplitter
+extends AbstractPnmSplitter
+{
+//  private static final Logger LOGGER = Logger.getLogger(PnmSplitter.class
+//      .getName());
+  protected AbstractPnmSplitter delegate;
+  
+  public PnmSplitter(DviSize unitSize, SplitImageWriter imageWriter)
+  {
+    super(unitSize, imageWriter);
+    delegate = null;
+  }
+  
+  @Override
+  public void beginSplitInternal(PnmHeader header, InputStream is) throws DviException
+  {
+    super.beginSplitInternal(header, is);
+    final int type = header.getType();
+    switch (type) {
+    case PnmHeader.PNM_TYPE_BITMAP_ASCII:
+      delegate = new PbmSplitter(getUnitSize(), imageWriter);
+      is = new PnmBitAsciiFilter(is);
+      break;
+    case PnmHeader.PNM_TYPE_GRAYMAP_ASCII:
+      delegate = new PgmSplitter(getUnitSize(), imageWriter);
+      is = new PnmByteAsciiFilter(is);
+      break;
+    case PnmHeader.PNM_TYPE_PIXMAP_ASCII:
+      delegate = new PpmSplitter(getUnitSize(), imageWriter);
+      is = new PnmByteAsciiFilter(is);
+      break;
+    case PnmHeader.PNM_TYPE_BITMAP_BINARY:  delegate = new PbmSplitter(getUnitSize(), imageWriter); break;
+    case PnmHeader.PNM_TYPE_GRAYMAP_BINARY: delegate = new PgmSplitter(getUnitSize(), imageWriter); break;
+    case PnmHeader.PNM_TYPE_PIXMAP_BINARY:  delegate = new PpmSplitter(getUnitSize(), imageWriter); break;
+    }
+    if (delegate != null) {
+      delegate.beginSplitInternal(header, is);
+    } else {
+      throw new IllegalStateException("Unsupported PNM image type: " + type);
+    }
+  }
+  
+  @Override
+  public void readFully(byte [] buf) throws IOException
+  {
+    delegate.readFully(buf);
+  }
+  
+  @Override
+  public void endSplitInternal() throws DviException
+  {
+    if (delegate != null) {
+      delegate.endSplitInternal();
+      delegate = null;
+    }
+  }
+  
+  @Override
+  protected byte [] createLineBuffer()
+  {
+    return delegate.createLineBuffer();
+  }
+  
+  @Override
+  protected BufferedImage createBufferedImage(DviRect box)
+  {
+    return delegate.createBufferedImage(box);
+  }
+  
+  @Override
+  protected int copyLineToDataBuffer(byte [] buf, int x, DviRect box, int i, DataBufferByte data)
+  {
+    return delegate.copyLineToDataBuffer(buf, x, box, i, data);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PpmSplitter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/pnm/PpmSplitter.java
new file mode 100644 (file)
index 0000000..6d62efb
--- /dev/null
@@ -0,0 +1,95 @@
+package jp.sourceforge.dvibrowser.dvicore.image.pnm;
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.io.InputStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+import jp.sourceforge.dvibrowser.dvicore.image.split.SplitImageWriter;
+
+public class PpmSplitter
+extends AbstractPnmSplitter
+{
+//  private static final Logger LOGGER = Logger.getLogger(PpmSplitter.class
+//      .getName());
+  public PpmSplitter(DviSize unitSize, SplitImageWriter imageWriter)
+  {
+    super(unitSize, imageWriter);
+  }
+  
+  private final int [] table = new int[256];
+  
+  @Override
+  public void beginSplitInternal(PnmHeader header, InputStream is) throws DviException
+  {
+    super.beginSplitInternal(header, is);
+    int maxval = header.getMaxValue();
+    for (int i=0; i<256; i++) {
+      table[i] = Math.max(Math.min(i * maxval / 255, maxval), 0);
+    }
+  }
+  
+  @Override
+  protected byte [] createLineBuffer()
+  {
+    return new byte [header.getWidth() * 3];
+  }
+  
+  @Override
+  protected BufferedImage createBufferedImage(DviRect box)
+  {
+    return new BufferedImage(box.width(), box.height(), BufferedImage.TYPE_3BYTE_BGR);
+  }
+  
+  @Override
+  protected int copyLineToDataBuffer(byte [] buf, int x, DviRect box, int i, DataBufferByte data)
+  {
+    final int w = box.width();
+    final int uw = w * 3;
+    final byte [] dst = data.getData();
+    for (int j=0; j<w; j++) {
+      dst[j * 3 + i * uw + 0] = (byte) table[byteToInt(buf[x + j * 3 + 2])]; // B
+      dst[j * 3 + i * uw + 1] = (byte) table[byteToInt(buf[x + j * 3 + 1])]; // G
+      dst[j * 3 + i * uw + 2] = (byte) table[byteToInt(buf[x + j * 3 + 0])]; // R
+    }
+    return uw;
+  }
+
+  private int byteToInt(byte b) {
+    if (b >= 0) return b;
+    return 256 + b;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/AbstractSplitPiece.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/AbstractSplitPiece.java
new file mode 100644 (file)
index 0000000..737c1d7
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+
+public abstract class AbstractSplitPiece
+implements SplitPiece
+{
+//  private static final Logger LOGGER = Logger
+//      .getLogger(AbstractSplitPiece.class.getName());
+  private final DviResolution res;
+  private final DviRectSplitter rectSplitter;
+  private final int row;
+  private final int col;
+  private final SplitImage splitImage;
+
+  public AbstractSplitPiece(SplitImage splitImage, DviResolution res, DviRectSplitter rectSplitter, int row, int col)
+  {
+    this.splitImage = splitImage;
+    this.res = res;
+    this.rectSplitter = rectSplitter;
+    this.row = row;
+    this.col = col;
+  }
+  
+  public DviResolution getResolution()
+  {
+    return res;
+  }
+
+  public DviRectSplitter getRectSplitter() {
+    return rectSplitter;
+  }
+
+  public int getRow() {
+    return row;
+  }
+
+  public int getColumn() {
+    return col;
+  }
+
+  public SplitImage getSplitImage() {
+    return splitImage;
+  }
+  
+  public DviRect getRect() throws DviException {
+    return getRectSplitter().getRectAt(row, col);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/DefaultSplitImageWriter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/DefaultSplitImageWriter.java
new file mode 100644 (file)
index 0000000..58ae2d7
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+import java.awt.image.RenderedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import javax.imageio.ImageIO;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+
+public class DefaultSplitImageWriter implements SplitImageWriter {
+//  private static final Logger LOGGER = Logger
+//      .getLogger(DefaultSplitImageWriter.class.getName());
+  private static class SplitImageImpl implements SplitImage {
+    private final DviRectSplitter rectSplitter;
+    private final ArrayList<SplitPiece> imagePieces = new ArrayList<SplitPiece>();
+    private final DviResolution res;
+
+    public SplitImageImpl(DviResolution res, DviRectSplitter rectSplitter) {
+      this.res = res;
+      this.rectSplitter = rectSplitter;
+    }
+
+    public Collection<SplitPiece> getPieces() throws DviException {
+      return Collections.unmodifiableCollection(imagePieces);
+    }
+
+    public DviRectSplitter getRectSplitter() throws DviException {
+      return rectSplitter;
+    }
+
+    public Iterator<SplitPiece> iterator() {
+      return imagePieces.iterator();
+    }
+
+    public DviRect getRect() throws DviException {
+      return rectSplitter.getRect();
+    }
+
+    public DviResolution getResolution() {
+      return res;
+    }
+  }
+  
+  private final File outputFile;
+  private final ImageFileConfig imgConf;
+  
+  protected DviRectSplitter rectSplitter;
+  protected SplitImageImpl splitImage;
+  private final DviResolution res;
+
+  public DefaultSplitImageWriter(File outputFile, ImageFileConfig imgConf, DviResolution res)
+  {
+    this.outputFile = outputFile;
+    this.imgConf = imgConf;
+    this.res = res;
+    this.rectSplitter = null;
+    this.splitImage = null;
+  }
+  
+  public ImageFileConfig getImageFileConfig() throws DviException {
+    return imgConf;
+  }
+
+  protected String generateFilename(int rows, int cols, DviSize unit, int row,
+      int col) throws DviException
+  {
+    String filename = getOutputFile().getName() + "-" + res.dpi() + "_" + res.shrinkFactor() + "-"
+        + unit.width() + "-" + unit.height() + "-" + rows + "-" + cols + "-"
+        + row + "-" + col + getImageFileConfig().getImageExtension();
+
+    return filename;
+  }
+
+  public File getOutputFile() {
+    return outputFile;
+  }
+
+
+  public void beginSplitImage(ImageSplitter ImageSplitter,
+      DviRectSplitter rectSplitter) throws DviException {
+    this.rectSplitter = rectSplitter;
+    this.splitImage = new SplitImageImpl(getResolution(), rectSplitter);
+  }
+
+
+  public void endSplitImage() throws DviException {
+  }
+
+  protected URLImagePiece writeImageToFile(RenderedImage img,
+      DviRectSplitter splitter, int row, int col) throws IOException,
+      DviException {
+    final int rows = splitter.getNumRows();
+    final int cols = splitter.getNumColumns();
+    final DviSize unit = splitter.getUnitSize();
+    String filename = generateFilename(rows, cols, unit, row, col);
+    File imgFile = new File(getOutputFile().getParentFile(), filename);
+    ImageIO.write(img, imgConf.getImageType(), imgFile);
+    return new URLImagePiece(splitImage, imgFile.toURL(), getResolution(), splitter, row, col);
+  }
+
+  public void writeImagePiece(RenderedImage img, int row, int col)
+      throws DviException {
+    try {
+      URLImagePiece piece = writeImageToFile(img, rectSplitter, row, col);
+      splitImage.imagePieces.add(piece);
+    } catch (IOException e) {
+      throw new DviException(e);
+    }
+  }
+  
+  public SplitImage getSplitImage() throws DviException {
+    return splitImage;
+  }
+
+  public DviResolution getResolution() {
+    return res;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/DviImage.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/DviImage.java
new file mode 100644 (file)
index 0000000..f253329
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+import java.awt.Image;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+public class DviImage
+implements SplitImage
+{
+  private static final Logger LOGGER = Logger.getLogger(DviImage.class
+      .getName());
+  private final Image img;
+  private final DviResolution res;
+  private DviRect rect;
+  private final ArrayList<SplitPiece> pieces = new ArrayList<SplitPiece>();
+  
+  public DviImage(Image img, int dpi)
+  {
+    if (img == null) throw new IllegalArgumentException("img can't be null");
+    this.img = img;
+    this.res = new DviResolution(dpi, 1);
+    pieces.add(new SplitPiece() {
+      public int getColumn() throws DviException {
+        return 0;
+      }
+
+      public Image getImage() throws DviException {
+        return DviImage.this.img;
+      }
+
+      public DviRect getRect() throws DviException {
+        return DviImage.this.getRect();
+      }
+
+      public DviRectSplitter getRectSplitter() throws DviException {
+        return DviImage.this.getRectSplitter();
+      }
+
+      public DviResolution getResolution() throws DviException {
+        return DviImage.this.getResolution();
+      }
+
+      public int getRow() throws DviException {
+        return 0;
+      }
+
+      public SplitImage getSplitImage() throws DviException {
+        return DviImage.this;
+      }
+    });
+  }
+
+  public Image getImage()
+  {
+    return img;
+  }
+
+  public int getDpi()
+  {
+    return res.dpi();
+  }
+
+  public Collection<SplitPiece> getPieces() throws DviException {
+    return pieces;
+  }
+
+  public synchronized DviRect getRect() throws DviException {
+    if (rect == null) {
+      rect = new DviRect(0, 0, img.getWidth(null), img.getHeight(null));
+    }
+    return rect;
+  }
+
+  public DviRectSplitter getRectSplitter() throws DviException {
+    DviRect r = getRect();
+    return new DviRectSplitter(r, r.size());
+  }
+
+  public DviResolution getResolution() {
+    return res;
+  }
+
+  public Iterator<SplitPiece> iterator() {
+    try {
+      return getPieces().iterator();
+    } catch (DviException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      // TODO: Think about better handle this.  Should we really use a RuntimeException?
+      throw new RuntimeException(e);
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/FileImagePiece.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/FileImagePiece.java
new file mode 100644 (file)
index 0000000..9382f97
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+import java.awt.Image;
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.imageio.ImageIO;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+public class FileImagePiece
+extends AbstractSplitPiece
+{
+  private static final Logger LOGGER = Logger.getLogger(FileImagePiece.class.getName());
+  
+  private final ImageFileConfig imageConf;
+  private final File file;
+
+  public FileImagePiece(SplitImage splitImage, File file, DviResolution res, ImageFileConfig imageConf, DviRectSplitter rectSplitter, int row, int col)
+  {
+    super(splitImage, res, rectSplitter, row, col);
+    this.file = file;
+    this.imageConf = imageConf;
+  }
+
+  public ImageFileConfig getImageFileConfig() {
+    return imageConf;
+  }
+
+  public Image getImage() throws DviException {
+    try {
+      Image img = ImageIO.read(file);
+      return img;
+    } catch (IOException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    }
+  }
+
+  public File getFile() {
+    return file;
+  }
+
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/ImageFileConfig.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/ImageFileConfig.java
new file mode 100644 (file)
index 0000000..5f5656e
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+public class ImageFileConfig {
+  public static final ImageFileConfig PNG = new ImageFileConfig("png", ".png");
+  public static final ImageFileConfig GIF = new ImageFileConfig("gif", ".gif");
+  public static final ImageFileConfig JPEG = new ImageFileConfig("jpeg", ".jpg");
+  
+  private final String imageType;
+  private final String imageExt;
+
+  public ImageFileConfig(String imageType, String imageExt)
+  {
+    if (imageType == null)
+      throw new IllegalArgumentException("imageType can't be null");
+    if (imageExt == null)
+      throw new IllegalArgumentException("imageExt can't be null");
+    
+    this.imageType = imageType;
+    this.imageExt = imageExt;
+  }
+
+  public String getImageType() {
+    return imageType;
+  }
+
+  public String getImageExtension() {
+    return imageExt;
+  }
+  
+  public boolean equals(Object o)
+  {
+    if (!(o instanceof ImageFileConfig))
+      return false;
+    
+    ImageFileConfig oo = (ImageFileConfig) o;
+    return oo.imageType.equals(imageType) && oo.imageExt.equals(imageExt);
+  }
+  
+  public int hashCode()
+  {
+    return imageType.hashCode() + 33*imageExt.hashCode();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/ImageSplitter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/ImageSplitter.java
new file mode 100644 (file)
index 0000000..9adfb3b
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+
+
+public interface ImageSplitter {
+  public DviSize getUnitSize() throws DviException;
+  public void splitImageFromStream(InputStream is) throws IOException, DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitImage.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitImage.java
new file mode 100644 (file)
index 0000000..057b55c
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+
+import java.util.Collection;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+
+
+public interface SplitImage
+extends Iterable<SplitPiece>
+{
+  public DviResolution getResolution();
+  public DviRect getRect() throws DviException;
+  public DviRectSplitter getRectSplitter() throws DviException;
+  public Collection<SplitPiece> getPieces() throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitImageUtils.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitImageUtils.java
new file mode 100644 (file)
index 0000000..b1aa9f7
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+
+
+public class SplitImageUtils {
+  public static void renderToGraphics(Graphics g, SplitImage img, int x, int y, double scale) throws DviException
+  {
+    if (g == null) throw new NullPointerException("Graphics object can't be null");
+    if (img == null) throw new NullPointerException("img can't be null");
+    
+    Graphics2D gg = (Graphics2D) g;
+    
+    DviSize unit = img.getRectSplitter().getUnitSize();
+    DviSize scaleUnit = unit.magnify(scale);
+    DviRect clip = DviRect.fromRectangle(g.getClipBounds());
+    
+    for (SplitPiece piece : img) {
+      int row = piece.getRow();
+      int col = piece.getColumn();
+      DviRect scaledRect = piece.getRect().magnify(scale).moveTo(x + col * scaleUnit.width, y + row * scaleUnit.height);
+      if (clip != null && !scaledRect.intersects(clip)) continue;
+      Rectangle r = scaledRect.toRectangle();
+      Image img2 = piece.getImage();
+      gg.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+          RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+      gg.drawImage(piece.getImage(), r.x, r.y, r.x + r.width + 1, r.y + r.height + 1, 0, 0, img2.getWidth(null), img2.getHeight(null), null);
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitImageWriter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitImageWriter.java
new file mode 100644 (file)
index 0000000..c51905e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+
+import java.awt.image.RenderedImage;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+
+
+public interface SplitImageWriter {
+  public void beginSplitImage(ImageSplitter ImageSplitter, DviRectSplitter rectSplitter) throws DviException;
+  public void endSplitImage() throws DviException;
+  public void writeImagePiece(RenderedImage img, int row, int col) throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitPiece.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/SplitPiece.java
new file mode 100644 (file)
index 0000000..e561295
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+import java.awt.Image;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+
+public interface SplitPiece
+{
+  public DviResolution getResolution() throws DviException;
+  public DviRectSplitter getRectSplitter() throws DviException;
+  public int getRow() throws DviException;
+  public int getColumn() throws DviException;
+  public Image getImage() throws DviException;
+  public DviRect getRect() throws DviException;
+  public SplitImage getSplitImage() throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/URLImagePiece.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/URLImagePiece.java
new file mode 100644 (file)
index 0000000..01f7d63
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+import java.awt.Image;
+import java.io.IOException;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.imageio.ImageIO;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+public class URLImagePiece
+extends AbstractSplitPiece
+{
+  private static Logger LOGGER = Logger.getLogger(URLImagePiece.class.getName());
+  
+  private final URL url;
+
+  public URLImagePiece(SplitImage splitImage, URL url, DviResolution res, DviRectSplitter rectSplitter, int row, int col)
+  {
+    super(splitImage, res, rectSplitter, row, col);
+    this.url = url;
+  }
+
+  public URL getURL() {
+    return url;
+  }
+
+  public Image getImage() throws DviException {
+    try {
+      Image img = ImageIO.read(url);
+      return img;
+    } catch (IOException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/ZipImagePiece.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/ZipImagePiece.java
new file mode 100644 (file)
index 0000000..d271e34
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+import java.awt.Image;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.imageio.ImageIO;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+public class ZipImagePiece
+extends AbstractSplitPiece
+{
+  private static Logger LOGGER = Logger.getLogger(ZipImagePiece.class.getName());
+  
+  private final File file;
+  private final String entryName;
+
+  public ZipImagePiece(SplitImage splitImage, File file, String entryName, DviResolution res, DviRectSplitter rectSplitter, int row, int col)
+  {
+    super(splitImage, res, rectSplitter, row, col);
+    this.file = file;
+    this.entryName = entryName;
+  }
+
+  public Image getImage() throws DviException {
+    try {
+      ZipFile zipFile = new ZipFile(file);
+      try {
+        ZipEntry entry = zipFile.getEntry(entryName);
+        if (entry != null) {
+          InputStream is = zipFile.getInputStream(entry);
+          try {
+            Image img = ImageIO.read(is);
+            return img;
+          } finally {
+            DviUtils.silentClose(is);
+          }
+        } else {
+          throw new DviException("zip entry not found: entryName=" + entryName + " file=" + file.getAbsolutePath());
+        }
+      } finally {
+        zipFile.close();
+      }
+    } catch (IOException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    }
+  }
+
+  public File getFile() {
+    return file;
+  }
+
+  public String getEntryName() {
+    return entryName;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/ZipSplitImageReader.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/ZipSplitImageReader.java
new file mode 100644 (file)
index 0000000..582a420
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+public class ZipSplitImageReader {
+  private static final Logger LOGGER = Logger
+      .getLogger(ZipSplitImageReader.class.getName());
+  private final File file;
+  
+  private static class SplitImageImpl implements SplitImage
+  {
+    private DviRectSplitter rectSplitter;
+    private final ArrayList<SplitPiece> pieces = new ArrayList<SplitPiece>();
+    private final File file;
+    private final Properties props;
+    private final DviResolution res;
+    
+    public SplitImageImpl(File file, Properties props, DviResolution res, DviRectSplitter rectSplitter)
+    {
+      this.file = file;
+      this.props = props;
+      this.res = res;
+      this.rectSplitter = rectSplitter;
+    }
+
+    public Collection<SplitPiece> getPieces() throws DviException {
+      return Collections.unmodifiableCollection(pieces);
+    }
+
+    public DviRectSplitter getRectSplitter() throws DviException {
+      return rectSplitter;
+    }
+
+    public Iterator<SplitPiece> iterator() {
+      return pieces.iterator();
+    }
+
+    public Properties getProperties() {
+      return props;
+    }
+
+    public DviRect getRect() throws DviException {
+      return rectSplitter.getRect();
+    }
+
+    public DviResolution getResolution() {
+      return res;
+    }
+
+    public File getFile() {
+      return file;
+    }
+  }
+  
+  public ZipSplitImageReader(File file)
+  {
+    this.file = file;
+  }
+
+  public SplitImage getSplitImage() throws DviException
+  {
+    try {
+      ZipFile zipFile = new ZipFile(file);
+      try {
+        String indexEntryName = "images/index.properties";
+        ZipEntry entry = zipFile.getEntry(indexEntryName);
+        if (entry == null) {
+          throw new DviException("Image bundle has no index file: " + file.getAbsolutePath());
+        }
+        Properties props = new Properties();
+        props.load(zipFile.getInputStream(entry));
+        String fmt = props.getProperty("image.format.name");
+        if ("image-bundle".equals(fmt)) {
+          final int dpi = Integer.parseInt(props.getProperty("image.hres.dpi"));
+          final int sf = Integer.parseInt(props.getProperty("image.hres.sf"));
+          final int width = Integer.parseInt(props.getProperty("image.total.width")); 
+          final int height = Integer.parseInt(props.getProperty("image.total.height")); 
+          final int uw = Integer.parseInt(props.getProperty("image.unit.width")); 
+          final int uh = Integer.parseInt(props.getProperty("image.unit.height")); 
+          final DviResolution res = new DviResolution(dpi, sf);
+          final DviSize unitSize = new DviSize(uw, uh);
+          final DviRect rect = new DviRect(0, 0, width, height);
+          final DviRectSplitter rectSplitter = new DviRectSplitter(rect, unitSize);
+          SplitImageImpl splitImage = new SplitImageImpl(file, props, res, rectSplitter);
+          { // populate the image with pieces.
+            int rows = rectSplitter.getNumRows();
+            int cols = rectSplitter.getNumColumns();
+            for (int i=0; i<rows; i++) {
+              for (int j=0; j<cols; j++) {
+                String entryName = props.getProperty("image.piece." + i + "." + j + ".file");
+                ZipImagePiece piece = new ZipImagePiece(splitImage, file, entryName, res, rectSplitter, i, j);
+                splitImage.pieces.add(piece);
+              }
+            }
+          }
+          return splitImage;
+        } else {
+          throw new DviException("Unrecognized format " + fmt + " in index file: " + indexEntryName + " of " + file.getAbsolutePath());
+        }
+      } finally {
+        zipFile.close();
+      }
+    } catch (MalformedURLException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    } catch (IOException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    }
+  }
+
+  public File getFile() {
+    return file;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/image/split/ZipSplitImageWriter.java b/src/jp/sourceforge/dvibrowser/dvicore/image/split/ZipSplitImageWriter.java
new file mode 100644 (file)
index 0000000..f2d4e97
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.image.split;
+
+import java.awt.Image;
+import java.awt.image.RenderedImage;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.imageio.ImageIO;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviRectSplitter;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+import jp.sourceforge.dvibrowser.dvicore.util.ZipBuilder;
+
+
+public class ZipSplitImageWriter implements SplitImageWriter {
+   private static final Logger LOGGER = Logger
+   .getLogger(ZipSplitImageWriter.class.getName());
+
+   private static class SplitImageImpl implements SplitImage {
+     private final DviRectSplitter rectSplitter;
+     private final DviResolution res;
+     private final ArrayList<SplitPiece> imagePieces = new ArrayList<SplitPiece>();
+
+     public SplitImageImpl(DviResolution res, DviRectSplitter rectSplitter) {
+       this.res = res;
+       this.rectSplitter = rectSplitter;
+     }
+
+     public DviResolution getResolution()
+     {
+       return res;
+     }
+
+     public Collection<SplitPiece> getPieces() throws DviException {
+       return Collections.unmodifiableCollection(imagePieces);
+     }
+
+     public DviRectSplitter getRectSplitter() throws DviException {
+       return rectSplitter;
+     }
+
+     public Iterator<SplitPiece> iterator() {
+       return imagePieces.iterator();
+     }
+
+    public DviRect getRect() throws DviException {
+      return rectSplitter.getRect();
+    }
+   }
+
+  private static class SplitPieceImpl extends AbstractSplitPiece {
+    private final String path;
+
+    public SplitPieceImpl(SplitImage splitImage, String path, DviResolution res,
+        DviRectSplitter rectSplitter, int row,
+        int col) {
+      super(splitImage, res, rectSplitter, row, col);
+      this.path = path;
+    }
+
+    public String getPath() {
+      return path;
+    }
+
+    public Image getImage() throws DviException {
+      return null;
+    }
+
+  }
+
+  private final String outputBasename;
+  private final ImageFileConfig imgConf;
+  private final String path = "images";
+
+  protected DviRectSplitter rectSplitter;
+  private SplitImageImpl splitImage;
+  private final ZipBuilder zip;
+  private final DviResolution res;
+
+  public ZipSplitImageWriter(String outputBasename, ImageFileConfig imgConf, DviResolution res, ZipBuilder zip) {
+    this.outputBasename = outputBasename;
+    this.imgConf = imgConf;
+    this.res = res;
+    this.zip = zip;
+    this.rectSplitter = null;
+    this.splitImage = null;
+  }
+
+  public ImageFileConfig getImageFileConfig() throws DviException {
+    return imgConf;
+  }
+
+  protected String generateFilename(int row, int col) throws DviException {
+    final DviSize unit = rectSplitter.getUnitSize();
+    final int rows = rectSplitter.getNumRows();
+    final int cols = rectSplitter.getNumColumns();
+    String filename = getOutputBasename() + "/" + res.dpi() + "_" + res.shrinkFactor() + "-"
+        + unit.width() + "-" + unit.height() + "-" + rows + "-" + cols + "-"
+        + row + "-" + col + getImageFileConfig().getImageExtension();
+
+    return filename;
+  }
+
+  public void beginSplitImage(ImageSplitter ImageSplitter,
+      DviRectSplitter rectSplitter) throws DviException {
+    if (rectSplitter == null) {
+      throw new IllegalArgumentException("rectSplitter is null");
+    }
+    this.rectSplitter = rectSplitter;
+    this.splitImage = new SplitImageImpl(res, rectSplitter);
+  }
+
+  public void endSplitImage() throws DviException {
+    try {
+      writeMetadata();
+      writeHtmlView();
+    } finally {
+      DviUtils.silentClose(zip);
+    }
+  }
+
+  protected void writeHtmlView() throws DviException {
+    try {
+      OutputStream os = getZipBuilder().openOutputStream("html/index.html");
+      try {
+        int rows = rectSplitter.getNumRows();
+        int cols = rectSplitter.getNumColumns();
+        DviRect totalRect = rectSplitter.getRect();
+
+        double baseWidth = 640.0;
+        double scale = (totalRect.width() > 0) ? (baseWidth / totalRect.width()) : baseWidth;
+
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
+        pw.print("<html><body><table cellpadding='0' cellspacing='0' border='0'>");
+        String closingTag = "";
+        for (SplitPiece piece : splitImage) {
+          if (piece instanceof SplitPieceImpl) {
+            SplitPieceImpl p = (SplitPieceImpl) piece;
+            String filename = "../" + p.getPath();
+            DviRect r = p.getRect();
+            double x = r.x() * scale;
+            double y = r.y() * scale;
+            int w = (int) Math.floor(r.width() * scale);
+            int h = (int) Math.floor(r.height() * scale);
+            if (piece.getColumn() == 0) {
+              pw.print(closingTag);
+              pw.printf("<tr>");
+              closingTag = "</tr>";
+            }
+            pw.printf("<td>", scale);
+            pw.printf("<img src='%s' alt='' galleryimg='no' style='width: %dpx; height: %dpx;' /></span>", filename, w, h);
+            pw.printf("</td>");
+          }
+        }
+        pw.print(closingTag);
+        pw.print("</table></body></html>");
+        pw.flush();
+        pw.close();
+      } finally {
+        DviUtils.silentClose(os);
+      }
+    } catch (IOException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    }
+  }
+
+  protected void writeMetadata() throws DviException {
+    try {
+      DviRect rect = rectSplitter.getRect();
+      DviSize unit = rectSplitter.getUnitSize();
+      // TODO: use XML instead of .properties.
+      Properties props = new Properties();
+      props.setProperty("image.format.name", "image-bundle");
+      props.setProperty("image.basepath", path);
+      props.setProperty("image.path", path + "/" + getOutputBasename()); // TODO:
+                                                                         // make
+                                                                         // the
+                                                                         // path
+                                                                         // computation
+                                                                         // a
+                                                                         // method.
+      props.setProperty("image.format.version", "0.1");
+      props.setProperty("image.total.width", String.valueOf(rect.width()));
+      props.setProperty("image.total.height", String.valueOf(rect.height()));
+      props.setProperty("image.unit.width", String.valueOf(unit.width()));
+      props.setProperty("image.unit.height", String.valueOf(unit.height()));
+      props.setProperty("image.hres.dpi", String.valueOf(res.dpi()));
+      props.setProperty("image.hres.sf", String.valueOf(res.shrinkFactor()));
+      props.setProperty("image.vres.dpi", String.valueOf(res.dpi()));
+      props.setProperty("image.vres.sf", String.valueOf(res.shrinkFactor()));
+      props.setProperty("image.piece.length", String.valueOf(splitImage
+          .getPieces().size()));
+      int rows = rectSplitter.getNumRows();
+      int cols = rectSplitter.getNumColumns();
+      props.setProperty("image.piece.rows", String.valueOf(rows));
+      props.setProperty("image.piece.cols", String.valueOf(cols));
+      for (SplitPiece piece : splitImage) {
+        if (piece instanceof SplitPieceImpl) {
+          SplitPieceImpl p = (SplitPieceImpl) piece;
+          String filename = p.getPath();
+          props.setProperty("image.piece." + piece.getRow() + "."
+              + piece.getColumn() + ".file", filename);
+        }
+      }
+
+      OutputStream os = getZipBuilder().openOutputStream(
+          "images/index.properties");
+      try {
+        props.store(os, "image-bundle");
+      } finally {
+        DviUtils.silentClose(os);
+      }
+    } catch (IOException e) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+      throw new DviException(e);
+    }
+  }
+
+  protected File createTempFile() throws IOException
+  {
+    // TODO: add a temporary file manager to  DviContext.
+    File file = File.createTempFile("zip-png", "tmp");
+    file.deleteOnExit();
+    return file;
+  }
+
+  public void writeImagePiece(RenderedImage img, int row, int col)
+      throws DviException {
+    try {
+      // TODO: Think about the directory structure of the output zip file.
+      // TODO: Compute md5 of piece image and store it.  Make a flag to denote if the data contains md5 or not.
+      String filename = path + "/" + generateFilename(row, col);
+      File tmpFile = createTempFile();
+      try {
+        ImageIO.write(img, imgConf.getImageType(), tmpFile);
+        getZipBuilder().write(filename, tmpFile);
+        SplitPiece piece = new SplitPieceImpl(splitImage, filename, res, rectSplitter, row, col);
+        splitImage.imagePieces.add(piece);
+      } finally {
+        tmpFile.delete();
+      }
+    } catch (IOException e) {
+      throw new DviException(e);
+    }
+  }
+
+  public ZipBuilder getZipBuilder() {
+    return zip;
+  }
+
+  public String getOutputBasename() {
+    return outputBasename;
+  }
+
+  public SplitImage getSplitImage()
+  {
+    return splitImage;
+  }
+
+  public DviResolution getResolution() {
+    return res;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/io/ByteArrayDviData.java b/src/jp/sourceforge/dvibrowser/dvicore/io/ByteArrayDviData.java
new file mode 100644 (file)
index 0000000..4ed0707
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.io;
+
+import java.io.ByteArrayInputStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontTable;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.DviData;
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+
+
+public class ByteArrayDviData
+implements DviData
+{
+  private final byte [] buf;
+  private final DviFontTable fontTable;
+  private final DviUnit dviUnit;
+
+  public ByteArrayDviData(byte [] buf)
+  {
+    this(buf, null, null);
+  }
+
+  public ByteArrayDviData(byte [] buf, DviUnit dviUnit)
+  {
+    this(buf, dviUnit, null);
+  }
+
+  public ByteArrayDviData(byte [] buf, DviUnit dviUnit, DviFontTable fontTable)
+  {
+    this.buf = buf;
+    this.dviUnit = dviUnit;
+    this.fontTable = fontTable;
+    if (buf == null)
+      throw new IllegalArgumentException
+        ("dvi data buffer cannot be null");
+  }
+
+  public DviInput getInput() {
+    return
+      new DviInputStreamReader(
+        new ByteArrayInputStream(buf)
+      );
+  }
+
+  public DviFontTable getFontTable() {
+    return fontTable;
+  }
+
+  public DviUnit getDviUnit() {
+    return dviUnit;
+  }
+
+  public long getDataSize() throws DviException
+  {
+    return buf.length;
+  }
+
+  public DviInput getInput(long start, long end) throws DviException
+  {
+    if (start < 0 || end < 0 || end < start)
+      throw new IllegalArgumentException
+        (String.format("Invalid byte range: (%d,%d)", start, end));
+    if (start >= buf.length)
+      throw new IllegalArgumentException
+      ("start position out of bounds: " + start);
+    if (end >= buf.length)
+      throw new IllegalArgumentException
+      ("end position out of bounds: " + end);
+
+    DviInputStreamReader in = new DviInputStreamReader(
+      new ByteArrayInputStream(
+          this.buf, (int) start, (int)(end - start) + 1
+        )
+      );
+    in.setOffset(start);
+    return in;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/io/DviByteBufferInput.java b/src/jp/sourceforge/dvibrowser/dvicore/io/DviByteBufferInput.java
new file mode 100644 (file)
index 0000000..76c7071
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.io;
+
+import java.io.IOException;
+import java.io.EOFException;
+import java.nio.ByteBuffer;
+
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+
+
+public class DviByteBufferInput
+implements DviInput
+{
+  private final ByteBuffer in;
+  public DviByteBufferInput(ByteBuffer in) {
+    this.in = in;
+  }
+
+  public void close()
+  throws IOException
+  {
+  }
+
+  private long offset = 0;
+  public long getOffset() { return offset; }
+  public void setOffset(long offset) { this.offset = offset; }
+
+  private long end = Long.MAX_VALUE;
+  public void setEnd(long end) { this.end = end; }
+
+  public boolean ready()
+  throws IOException
+  {
+    return (offset <= end && in.hasRemaining());
+  }
+
+  public int readU1()
+  throws IOException
+  {
+    if (offset > end)
+      throw new EOFException();
+    int c = in.get();
+    if (c < 0) c += 256;
+    offset++;
+    return c;
+  }
+
+  public void readFully(byte [] buf)
+  throws IOException
+  {
+    if (buf == null) return;
+    if (buf.length <= 0) return;
+    if (offset + buf.length - 1> end)
+      throw new EOFException();
+    in.get(buf);
+    offset += buf.length;
+  }
+
+  public int readU(int len)
+  throws IOException
+  {
+    int v = 0;
+
+    if (len <= 0 || len > 4) 
+      throw new IllegalArgumentException
+        ("illegal value of len.");
+
+    while (len > 0) {
+      v = (v << 8) | readU1();
+      len--;
+    }
+
+    return v;
+  }
+
+  public int readS(int len)
+  throws IOException
+  {
+    int bits = len * 8;
+    int a = 1;
+    int v = 0;
+
+    if (len <= 0 || len > 4)
+      throw new IllegalArgumentException
+        ("illegal value of len.");
+
+    while (len > 0) {
+      v = (v << 8) | readU1();
+      a <<= 8;
+      len--;
+    }
+    if (0 != (v & (1 << (bits - 1)))) {
+      v -= a;
+    }
+
+    return v;
+  }
+
+  public int readU2()
+  throws IOException
+  {
+    return ((readU1() << 8)  |
+             readU1());
+  }
+
+  public int readU3()
+  throws IOException
+  {
+    return ((readU1() << 16) |
+            (readU1() << 8)  |
+             readU1());
+  }
+
+  public int readU4()
+  throws IOException
+  {
+    return ((readU1() << 24) |
+            (readU1() << 16) |
+            (readU1() << 8)  |
+             readU1());
+  }
+
+  public int readS1()
+  throws IOException
+  {
+    return readS(1);
+  }
+
+  public int readS2()
+  throws IOException
+  {
+    return readS(2);
+  }
+
+  public int readS3()
+  throws IOException
+  {
+    return readS(3);
+  }
+
+  public int readS4()
+  throws IOException
+  {
+    return readS(4);
+  }
+
+  public void skip(int len)
+  throws IOException
+  {
+    if (len <= 0) return;
+    byte [] buf = new byte[len];
+    readFully(buf);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/io/DviInputStreamReader.java b/src/jp/sourceforge/dvibrowser/dvicore/io/DviInputStreamReader.java
new file mode 100644 (file)
index 0000000..6ea1086
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.io;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+
+
+public class DviInputStreamReader
+implements DviInput
+{
+  private final InputStream in;
+  public DviInputStreamReader(InputStream in)
+  {
+    this.in = in;
+  }
+
+  public void close()
+  throws IOException
+  {
+    in.close();
+  }
+
+  private long offset = 0;
+  public long getOffset() { return offset; }
+  public void setOffset(long offset) { this.offset = offset; }
+
+  private long end = Long.MAX_VALUE;
+  public void setEnd(long end) { this.end = end; }
+
+/*
+  public boolean ready() throws IOException {
+    return (offset <= end); // && 0 < in.available());
+  }
+  */
+
+  public int readU1()
+  throws IOException
+  {
+    if (offset > end)
+      throw new EOFException();
+    int c = in.read();
+    if (c < 0)
+      throw new EOFException();
+    offset++;
+    return c;
+  }
+
+  public void readFully(byte [] buf)
+  throws IOException
+  {
+    if (buf == null) return;
+    if (buf.length <= 0) return;
+    if (offset + buf.length - 1> end)
+      throw new EOFException();
+    if (buf.length != in.read(buf))
+      throw new EOFException
+        ("while filling the buffer.");
+    offset += buf.length;
+  }
+
+  public int readU(int len)
+  throws IOException
+  {
+    int v = 0;
+
+    if (len <= 0 || len > 4) 
+      throw new IllegalArgumentException
+        ("illegal value of len: " + len);
+
+    while (len > 0) {
+      v = (v << 8) | readU1();
+      len--;
+    }
+
+    return v;
+  }
+
+  public int readS(int len)
+  throws IOException
+  {
+    int bits = len * 8;
+    int a = 1;
+    int v = 0;
+
+    if (len <= 0 || len > 4)
+      throw new IllegalArgumentException
+        ("illegal value of len: " + len);
+
+    while (len > 0) {
+      v = (v << 8) | readU1();
+      a <<= 8;
+      len--;
+    }
+    if (0 != (v & (1 << (bits - 1)))) {
+      v -= a;
+    }
+
+    return v;
+  }
+
+  public int readU2()
+  throws IOException
+  {
+    return ((readU1() << 8)  |
+             readU1());
+  }
+
+  public int readU3()
+  throws IOException
+  {
+    return ((readU1() << 16) |
+            (readU1() << 8)  |
+             readU1());
+  }
+
+  public int readU4()
+  throws IOException
+  {
+    return ((readU1() << 24) |
+            (readU1() << 16) |
+            (readU1() << 8)  |
+             readU1());
+  }
+
+  public int readS1()
+  throws IOException
+  {
+    return readS(1);
+  }
+
+  public int readS2()
+  throws IOException
+  {
+    return readS(2);
+  }
+
+  public int readS3()
+  throws IOException
+  {
+    return readS(3);
+  }
+
+  public int readS4()
+  throws IOException
+  {
+    return readS(4);
+  }
+
+  public void skip(int len)
+  throws IOException
+  {
+    long skipped = in.skip(len);
+    if (skipped > 0) {
+      offset += skipped;
+    }
+    long left = len - skipped;
+    while (left > 0) {
+      readU1();
+      left--;
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/io/DviRandomAccessFileInput.java b/src/jp/sourceforge/dvibrowser/dvicore/io/DviRandomAccessFileInput.java
new file mode 100644 (file)
index 0000000..3d6a30a
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.io;
+
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.io.EOFException;
+
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+
+
+public class DviRandomAccessFileInput
+implements DviInput
+{
+  private final RandomAccessFile in;
+
+  public DviRandomAccessFileInput(RandomAccessFile in)
+  {
+    this.in = in;
+  }
+
+  public void close()
+  throws IOException
+  {
+    in.close();
+  }
+
+  private long offset = 0;
+  public long getOffset() { return offset; }
+  public void setOffset(long offset) { this.offset = offset; }
+
+  private long end = Long.MAX_VALUE;
+  public void setEnd(long end) { this.end = end; }
+
+  public boolean ready()
+  throws IOException
+  {
+    return (offset <= end && (in.getFilePointer() < in.length()));
+  }
+
+  public int readU1()
+  throws IOException
+  {
+    if (offset > end)
+      throw new EOFException();
+    int c = in.readUnsignedByte();
+    if (c < 0) c += 256;
+    offset++;
+    return c;
+  }
+
+  public void readFully(byte [] buf)
+  throws IOException
+  {
+    if (buf == null) return;
+    if (buf.length <= 0) return;
+    if (offset + buf.length - 1> end)
+      throw new EOFException();
+    in.readFully(buf);
+    offset += buf.length;
+  }
+
+  public int readU(int len)
+  throws IOException
+  {
+    int v = 0;
+
+    if (len <= 0 || len > 4) 
+      throw new IllegalArgumentException
+        ("illegal value of len.");
+
+    while (len > 0) {
+      v = (v << 8) | readU1();
+      len--;
+    }
+
+    return v;
+  }
+
+  public int readS(int len)
+  throws IOException
+  {
+    int bits = len * 8;
+    int a = 1;
+    int v = 0;
+
+    if (len <= 0 || len > 4)
+      throw new IllegalArgumentException
+        ("illegal value of len.");
+
+    while (len > 0) {
+      v = (v << 8) | readU1();
+      a <<= 8;
+      len--;
+    }
+    if (0 != (v & (1 << (bits - 1)))) {
+      v -= a;
+    }
+
+    return v;
+  }
+
+  public int readU2()
+  throws IOException
+  {
+    return ((readU1() << 8)  |
+             readU1());
+  }
+
+  public int readU3()
+  throws IOException
+  {
+    return ((readU1() << 16) |
+            (readU1() << 8)  |
+             readU1());
+  }
+
+  public int readU4()
+  throws IOException
+  {
+    return ((readU1() << 24) |
+            (readU1() << 16) |
+            (readU1() << 8)  |
+             readU1());
+  }
+
+  public int readS1()
+  throws IOException
+  {
+    return readS(1);
+  }
+
+  public int readS2()
+  throws IOException
+  {
+    return readS(2);
+  }
+
+  public int readS3()
+  throws IOException
+  {
+    return readS(3);
+  }
+
+  public int readS4()
+  throws IOException
+  {
+    return readS(4);
+  }
+
+  public void skip(int len)
+  throws IOException
+  {
+    in.seek(in.getFilePointer() + len-1);
+    // TODO: use in.skipBytes();
+    offset += len - 1;
+    if (!ready()) {
+      throw new EOFException();
+    }
+    readU1();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/plat/cygwin/CygwinUtils.java b/src/jp/sourceforge/dvibrowser/dvicore/plat/cygwin/CygwinUtils.java
new file mode 100644 (file)
index 0000000..bc99303
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.plat.cygwin;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.util.CommandShell;
+import jp.sourceforge.dvibrowser.dvicore.util.CommandShellHandler;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+public class CygwinUtils {
+  private static final Logger LOGGER = Logger.getLogger(CygwinUtils.class
+      .getName());
+  
+  public static String posixPathToJavaPath(String posixPath)
+  throws IOException, InterruptedException, DviException
+  {
+    CommandShell cs = new CommandShell();
+    ArrayList<String> list = new ArrayList<String>();
+    list.add("cygpath");
+    list.add("-w");
+    list.add(posixPath);
+    cs.setCommandLine(list);
+    final ArrayList<String> outputs = new ArrayList<String>();
+    cs.setHandler(new CommandShellHandler() {
+      public void handleStderr(InputStream in) throws IOException {
+        DviUtils.logLinesFromStream("cygpath stderr", in, LOGGER, Level.FINE);
+      }
+      public void handleStdout(InputStream in) throws IOException {
+        String [] lines = DviUtils.readLinesFromStream(in);
+        for (String line : lines) {
+          outputs.add(line);
+        }
+      }
+      public void handleStdin(OutputStream out) throws IOException {
+        out.close();
+      }
+    });
+    int ret = cs.execute();
+    if (ret != 0) {
+      LOGGER.warning("command failed: " + DviUtils.join(" ", list));
+      return null;
+    } else {
+      if (outputs.size() > 0) {
+        return outputs.get(0);
+      } else {
+        return null;
+      }
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/AbstractDevice.java b/src/jp/sourceforge/dvibrowser/dvicore/render/AbstractDevice.java
new file mode 100644 (file)
index 0000000..154f0b4
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import java.util.Stack;
+
+import jp.sourceforge.dvibrowser.dvicore.DviColor;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviPoint;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.Device;
+
+
+public abstract class AbstractDevice
+implements Device
+{
+  private final DviResolution res;
+  public AbstractDevice(DviResolution res) {
+    this.res = res;
+  }
+  public DviResolution getResolution()
+  throws DviException {
+    return res;
+  }
+
+  protected DviPoint point = DviPoint.ORIGIN;
+  public DviPoint getReferencePoint() throws DviException {
+    return point;
+  }
+  public void setReferencePoint(DviPoint point) throws DviException {
+    this.point = point;
+  }
+
+  public void translate(DviPoint p) throws DviException {
+    point = point.translate(p);
+  }
+  public void translate(int dx, int dy) throws DviException {
+    point = point.translate(dx, dy);
+  }
+
+  private DviColor color = new DviColor(0, 0, 0);
+  public void setColor(DviColor color) throws DviException
+  {
+    this.color = color;
+  }
+  public DviColor getColor() throws DviException
+  {
+    return color;
+  }
+
+  protected final Stack<StackItem> stack = new Stack<StackItem>();
+
+  public void save() throws DviException {
+    stack.push(new StackItem(color, point));
+  }
+  public void restore() throws DviException {
+    if (stack.empty())
+      throw new IllegalStateException
+        ("stack underflow");
+
+    StackItem it = stack.pop();
+    color = it.color;
+    point = it.point;
+  }
+
+  private static class StackItem
+  {
+    private final DviColor color;
+    private final DviPoint point;
+    public StackItem(DviColor color, DviPoint point)
+    {
+      this.color = color;
+      this.point = point;
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/BasicExecutor.java b/src/jp/sourceforge/dvibrowser/dvicore/render/BasicExecutor.java
new file mode 100644 (file)
index 0000000..3c044ff
--- /dev/null
@@ -0,0 +1,713 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import java.io.EOFException;
+import java.io.IOException;
+
+import jp.sourceforge.dvibrowser.dvicore.DviByteRange;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviData;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutor;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorHandler;
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviCommand;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+
+
+// TODO: support logging
+
+public class BasicExecutor
+extends DviObject
+implements DviExecutor
+{
+  protected static class DviExecutorContextImpl
+  extends DviObject
+  implements DviExecutorContext
+  {
+    private DviData data;
+    private DviInput in;
+    private DviExecutorHandler handler;
+
+    private int command;
+    private long commandBegin;
+    private long commandEnd;
+    private boolean terminate = false;
+    
+    public DviExecutorContextImpl(DviContextSupport dcs)
+    {
+      super(dcs);
+    }
+
+    public DviData getData() { return data; }
+    public int getCommand() { return command; }
+    public DviByteRange getCommandRange() {
+      return new DviByteRange(commandBegin, commandEnd);
+    }
+    public void setTerminate(boolean f) { terminate = f; }
+
+    private void commandDetermined() {
+      commandEnd = in.getOffset() - 1;
+    }
+  }
+
+  public BasicExecutor(DviContextSupport dcs)
+  {
+    super(dcs);
+  }
+
+  private static final DviExecutorHandler defaultHandler
+    = new EmptyDviExecutorHandler();
+
+  public void execute(DviData data)
+  throws DviException
+  {
+    execute(data, null);
+  }
+
+  private DviExecutorContextImpl ctx;
+  protected DviExecutorContextImpl getExecutorContext()
+  {
+    return ctx;
+  }
+
+  public void execute(DviData data, DviExecutorHandler handler)
+  throws DviException
+  {
+    ctx = new DviExecutorContextImpl(this);
+    ctx.data = data;
+    ctx.handler = (handler != null) ? handler : defaultHandler;
+    ctx.in = data.getInput();
+    ctx.terminate = false;
+
+    if (ctx.handler == null)
+      throw new NullPointerException
+        ("handler is null");
+    if (ctx.data == null)
+      throw new NullPointerException
+        ("data is null");
+    if (ctx.in == null)
+      throw new NullPointerException
+        ("input is null");
+
+    try {
+      begin(ctx);
+      try {
+        while (!ctx.terminate) {
+          executeOneCommand(ctx);
+        }
+      } catch (EOFException ex) {
+        // ignored.
+      } finally {
+        end();
+      }
+    } catch(DviException ex) {
+      throw ex;
+    } catch(Throwable ex) {
+      ex.printStackTrace();
+      throw new DviException(ex);
+    } finally {
+      try {
+        ctx.in.close();
+      } catch (IOException ex) {
+        // ignored.
+      }
+    }
+  }
+
+  protected void executeOneCommand(DviExecutorContextImpl ctx)
+  throws IOException, DviException
+  {
+    final int t;
+    final DviInput in = ctx.in;
+    
+    ctx.commandBegin = ctx.commandEnd = in.getOffset();
+    t = ctx.command = in.readU1();
+
+    if (t <= 127) {
+      if (wantSet()) {
+        ctx.commandDetermined();
+        doSet(t);
+      }
+    } else if (171 <= t && t <= 234) {
+      if (wantSelectFont()) {
+        ctx.commandDetermined();
+        doSelectFont(t - 171);
+      }
+    } else {
+      switch(t) {
+        case DviCommand.DVI_SET4:
+        case DviCommand.DVI_SET3:
+        case DviCommand.DVI_SET2:
+        case DviCommand.DVI_SET1: {
+          final int alen = t - DviCommand.DVI_SET1 + 1;
+          if (wantSet()) {
+            final int code;
+
+            code = in.readU(alen);
+            ctx.commandDetermined();
+            doSet(code);
+          } else {
+            in.skip(alen);
+          }
+          break;
+        }
+
+        case DviCommand.DVI_PUT4:
+        case DviCommand.DVI_PUT3:
+        case DviCommand.DVI_PUT2:
+        case DviCommand.DVI_PUT1: {
+          final int alen = t - DviCommand.DVI_PUT1 + 1;
+          if (wantPut()) {
+            final int code;
+  
+            code = in.readU(alen);
+            ctx.commandDetermined();
+            doPut(code);
+          } else {
+            in.skip(alen);
+          }
+          break;
+        }
+
+        case DviCommand.DVI_FONT4:
+        case DviCommand.DVI_FONT3:
+        case DviCommand.DVI_FONT2:
+        case DviCommand.DVI_FONT1: {
+          final int alen = t - DviCommand.DVI_FONT1 + 1;
+          if (wantSelectFont()) {
+            final int fn;
+
+            fn = in.readU(alen);
+            ctx.commandDetermined();
+            doSelectFont(fn);
+          } else {
+            in.skip(alen);
+          }
+          break;
+        }
+
+        case DviCommand.DVI_XXX4:
+        case DviCommand.DVI_XXX3:
+        case DviCommand.DVI_XXX2:
+        case DviCommand.DVI_XXX1: {
+          final int alen = t - DviCommand.DVI_XXX1 + 1;
+          final int k;
+          k = in.readU(alen);
+          if (wantSpecial()) {
+            final byte [] xxx = new byte[k];
+            in.readFully(xxx);
+
+            ctx.commandDetermined();
+            doSpecial(xxx);
+          } else {
+            in.skip(alen + k);
+          }
+          break;
+        }
+
+        case DviCommand.DVI_SET_RULE: {
+          if (wantSetRule()) {
+            final int h, w;
+
+            h = in.readS4();
+            w = in.readS4();
+
+            ctx.commandDetermined();
+            doSetRule(w, h);
+          } else {
+            in.skip(4 + 4);
+          }
+          break;
+        }
+
+        case DviCommand.DVI_PUT_RULE: {
+          if (wantPutRule()) {
+            final int h, w;
+
+            h = in.readS4();
+            w = in.readS4();
+
+            ctx.commandDetermined();
+            doPutRule(w, h);
+          } else {
+            in.skip(4 + 4);
+          }
+          break;
+        }
+
+        case DviCommand.DVI_FNT_DEF4:
+        case DviCommand.DVI_FNT_DEF3:
+        case DviCommand.DVI_FNT_DEF2:
+        case DviCommand.DVI_FNT_DEF1: {
+          final int alen = t - DviCommand.DVI_FNT_DEF1 + 1;
+          if (wantDefineFont()) {
+            final int fn, cs, ss, ds, al, nl;
+            final byte [] fontName;
+
+            fn = in.readU(alen);
+            cs = in.readS4();
+            ss = in.readS4();
+            ds = in.readS4();
+            al = in.readU1();
+            nl = in.readU1();
+
+            fontName = new byte[al+nl];
+            in.readFully(fontName);
+
+            ctx.commandDetermined();
+            doDefineFont(
+              fn,
+              DviFontSpec.getInstance(cs, ss, ds, al, nl, fontName)
+            );
+          } else {
+            final int al, nl;
+            in.skip(alen + 4 + 4 + 4);
+            al = in.readU1();
+            nl = in.readU1();
+            in.skip(al + nl);
+          }
+          break;
+        }
+
+        case DviCommand.DVI_RIGHT4:
+        case DviCommand.DVI_RIGHT3:
+        case DviCommand.DVI_RIGHT2:
+        case DviCommand.DVI_RIGHT1: {
+          final int alen = t - DviCommand.DVI_RIGHT1 + 1;
+          if (wantRight()) {
+            final int dh;
+
+            dh = in.readS(alen);
+
+            ctx.commandDetermined();
+            doRight(dh);
+          } else {
+            in.skip(alen);
+          }
+          break;
+        }
+
+        case DviCommand.DVI_W4:
+        case DviCommand.DVI_W3:
+        case DviCommand.DVI_W2:
+        case DviCommand.DVI_W1: {
+          final int alen = t - DviCommand.DVI_W1 + 1;
+          if (wantW()) {
+            final int dh;
+
+            dh = in.readS(alen);
+
+            ctx.commandDetermined();
+            doW(dh);
+          } else {
+            in.skip(alen);
+          }
+          break;
+        }
+        case DviCommand.DVI_W0: {
+          if (wantW0()) {
+            ctx.commandDetermined();
+            doW0();
+          }
+          break;
+        }
+
+        case DviCommand.DVI_X4:
+        case DviCommand.DVI_X3:
+        case DviCommand.DVI_X2:
+        case DviCommand.DVI_X1: {
+          final int alen = t - DviCommand.DVI_X1 + 1;
+          if (wantX()) {
+            final int dh;
+
+            dh = in.readS(alen);
+
+            ctx.commandDetermined();
+            doX(dh);
+          } else {
+            in.skip(alen);
+          }
+          break;
+        }
+        case DviCommand.DVI_X0: {
+          if (wantX0()) {
+            ctx.commandDetermined();
+            doX0();
+          }
+          break;
+        }
+
+
+        case DviCommand.DVI_DOWN4:
+        case DviCommand.DVI_DOWN3:
+        case DviCommand.DVI_DOWN2:
+        case DviCommand.DVI_DOWN1: {
+          final int alen = t - DviCommand.DVI_DOWN1 + 1;
+          if (wantDown()) {
+            final int dv;
+
+            dv = in.readS(alen);
+
+            ctx.commandDetermined();
+            doDown(dv);
+          } else {
+            in.skip(alen);
+          }
+          break;
+        }
+
+        case DviCommand.DVI_Y4:
+        case DviCommand.DVI_Y3:
+        case DviCommand.DVI_Y2:
+        case DviCommand.DVI_Y1: {
+          final int alen = t - DviCommand.DVI_Y1 + 1;
+          if (wantY()) {
+            final int dv;
+
+            dv = in.readS(alen);
+
+            ctx.commandDetermined();
+            doY(dv);
+          } else {
+            in.skip(alen);
+          }
+          break;
+        }
+        case DviCommand.DVI_Y0: {
+          if (wantY0()) {
+            ctx.commandDetermined();
+            doY0();
+          }
+          break;
+        }
+
+        case DviCommand.DVI_Z4:
+        case DviCommand.DVI_Z3:
+        case DviCommand.DVI_Z2:
+        case DviCommand.DVI_Z1: {
+          final int alen = t - DviCommand.DVI_Z1 + 1;
+          if (wantZ()) {
+            final int dv;
+
+            dv = in.readS(alen);
+
+            ctx.commandDetermined();
+            doZ(dv);
+          } else {
+            in.skip(alen);
+          }
+          break;
+        }
+        case DviCommand.DVI_Z0: {
+          if (wantZ0()) {
+            ctx.commandDetermined();
+            doZ0();
+          }
+          break;
+        }
+
+        case DviCommand.DVI_PUSH: {
+          if (wantPush()) {
+            ctx.commandDetermined();
+            doPush();
+          }
+          break;
+        }
+        case DviCommand.DVI_POP: {
+          if (wantPop()) {
+            ctx.commandDetermined();
+            doPop();
+          }
+          break;
+        }
+
+        case DviCommand.DVI_NOP: {
+          if (wantNop()) {
+            ctx.commandDetermined();
+            doNop();
+          }
+          break;
+        }
+
+        case DviCommand.DVI_BOP: {
+          if (wantBop()) {
+            final int [] count = new int [10];
+            final int backPointer;
+
+            for (int i=0; i<10; i++) 
+              count[i] = in.readS4();
+            backPointer = in.readS4();
+
+            ctx.commandDetermined();
+            doBop(
+              new DviBop(count, backPointer)
+            );
+          } else {
+            in.skip(10 * 4 + 4);
+          }
+          break;
+        }
+        case DviCommand.DVI_EOP: {
+          if (wantEop()) {
+            ctx.commandDetermined();
+            doEop();
+          }
+          break;
+        }
+        case DviCommand.DVI_PRE: {
+          if (wantPre()) {
+            final int idByte, num, den, mag, commentSize;
+
+            idByte      = in.readU1();
+            num         = in.readS4();
+            den         = in.readS4();
+            mag         = in.readS4();
+            commentSize = in.readU1();
+
+            final byte [] comment = new byte [commentSize];
+            in.readFully(comment);
+
+            ctx.commandDetermined();
+            doPre(
+              new DviPreamble(
+                idByte,
+                DviUnit.getInstance(num, den, mag),
+                comment
+              )
+            );
+          } else {
+            final int commentSize;
+            in.skip(1 + 4 + 4 + 4);
+            commentSize = in.readU1();
+            in.skip(commentSize);
+          }
+          break;
+        }
+        case DviCommand.DVI_POST: {
+          if (wantPost()) {
+            final int firstBackPointer;
+            final int num, den, mag, maxV, maxH, maxStackDepth, totalPages;
+
+            firstBackPointer = in.readS4();
+            num              = in.readS4();
+            den              = in.readS4();
+            mag              = in.readS4();
+            maxV             = in.readS4();
+            maxH             = in.readS4();
+            maxStackDepth    = in.readU2();
+            totalPages       = in.readU2();
+
+            ctx.commandDetermined();
+            doPost(
+              new DviPostamble(
+                firstBackPointer,
+                DviUnit.getInstance(num, den, mag),
+                maxV, maxH,
+                maxStackDepth,
+                totalPages
+              )
+            );
+          } else {
+            in.skip(6 * 4 + 2 * 2);
+          }
+          break;
+        }
+        case DviCommand.DVI_POST_POST: {
+          if (wantPostPost()) {
+            final int postamblePointer;
+            final int idByte;
+
+            postamblePointer = in.readS4();
+            idByte           = in.readU1();
+
+            ctx.commandDetermined();
+            doPostPost(
+              new DviPostPost(
+                postamblePointer,
+                idByte
+              )
+            );
+          } else {
+            in.skip(4 + 1);
+          }
+          ctx.setTerminate(true);
+          break;
+        }
+        case DviCommand.DVI_UNDEF1:
+        case DviCommand.DVI_UNDEF2:
+        case DviCommand.DVI_UNDEF3:
+        case DviCommand.DVI_UNDEF4:
+        case DviCommand.DVI_UNDEF5: {
+          ctx.commandDetermined();
+          /* TODO: handle these commands. */
+          break;
+        }
+          
+        case DviCommand.DVI_UNDEF6: {
+          ctx.commandDetermined();
+          /* FIXME: handle TDIR */
+          break;
+        }
+
+        default:
+          /* not reached. */
+          throw new IllegalStateException
+            ("Illegal executer state.");
+      }
+    }
+  }
+
+  public boolean wantSet()        { return true; }
+  public boolean wantPut()        { return true; }
+  public boolean wantSelectFont() { return true; }
+  public boolean wantSpecial()    { return true; }
+  public boolean wantSetRule()    { return true; }
+  public boolean wantPutRule()    { return true; }
+  public boolean wantDefineFont() { return true; }
+  public boolean wantRight()      { return true; }
+  public boolean wantW()          { return true; }
+  public boolean wantW0()         { return true; }
+  public boolean wantX()          { return true; }
+  public boolean wantX0()         { return true; }
+  public boolean wantDown()       { return true; }
+  public boolean wantY()          { return true; }
+  public boolean wantY0()         { return true; }
+  public boolean wantZ()          { return true; }
+  public boolean wantZ0()         { return true; }
+  public boolean wantPush()       { return true; }
+  public boolean wantPop()        { return true; }
+  public boolean wantNop()        { return true; }
+  public boolean wantBop()        { return true; }
+  public boolean wantEop()        { return true; }
+  public boolean wantPre()        { return true; }
+  public boolean wantPost()       { return true; }
+  public boolean wantPostPost()   { return true; }
+
+  public void begin(DviExecutorContext ctx) throws DviException {
+    this.ctx.handler.begin(ctx);
+  }
+  public void end() throws DviException {
+    ctx.handler.end();
+  }
+
+  public void doSet(int code) throws DviException {
+    ctx.handler.doSet(code);
+  }
+  public void doSetRule(int w, int h) throws DviException {
+    ctx.handler.doSetRule(w, h);
+  }
+  public void doPut(int code) throws DviException {
+    ctx.handler.doPut(code);
+  }
+  public void doPutRule(int w, int h) throws DviException {
+    ctx.handler.doPutRule(w, h);
+  }
+  public void doNop() throws DviException {
+    ctx.handler.doNop();
+  }
+
+  public void doSelectFont(int fn) throws DviException {
+    ctx.handler.doSelectFont(fn);
+  }
+  public void doDefineFont(int fn, DviFontSpec fs) throws DviException {
+    ctx.handler.doDefineFont(fn, fs);
+  }
+
+  public void doPush() throws DviException {
+    ctx.handler.doPush();
+  }
+  public void doPop() throws DviException {
+    ctx.handler.doPop();
+  }
+
+  public void doPre(DviPreamble preamble) throws DviException {
+    ctx.handler.doPre(preamble);
+  }
+  public void doBop(DviBop bop) throws DviException {
+    ctx.handler.doBop(bop);
+  }
+  public void doEop() throws DviException {
+    ctx.handler.doEop();
+  }
+  public void doPost(DviPostamble postamble) throws DviException {
+    ctx.handler.doPost(postamble);
+  }
+  public void doPostPost(DviPostPost postPost) throws DviException {
+    ctx.handler.doPostPost(postPost);
+  }
+
+  public void doRight(int by) throws DviException {
+    ctx.handler.doRight(by);
+  }
+  public void doW(int by) throws DviException {
+    ctx.handler.doW(by);
+  }
+  public void doW0() throws DviException {
+    ctx.handler.doW0();
+  }
+  public void doX(int by) throws DviException {
+    ctx.handler.doX(by);
+  }
+  public void doX0() throws DviException {
+    ctx.handler.doX0();
+  }
+
+  public void doDown(int by) throws DviException {
+    ctx.handler.doDown(by);
+  }
+  public void doY(int by) throws DviException {
+    ctx.handler.doY(by);
+  }
+  public void doY0() throws DviException {
+    ctx.handler.doY0();
+  }
+  public void doZ(int by) throws DviException {
+    ctx.handler.doZ(by);
+  }
+  public void doZ0() throws DviException {
+    ctx.handler.doZ0();
+  }
+
+  public void doSpecial(byte [] xxx) throws DviException {
+    ctx.handler.doSpecial(xxx);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/BasicGeometer.java b/src/jp/sourceforge/dvibrowser/dvicore/render/BasicGeometer.java
new file mode 100644 (file)
index 0000000..66a078e
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import java.util.Stack;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviFontTable;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviRegister;
+import jp.sourceforge.dvibrowser.dvicore.api.DevicePainter;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorContext;
+import jp.sourceforge.dvibrowser.dvicore.api.Geometer;
+import jp.sourceforge.dvibrowser.dvicore.api.GeometerContext;
+import jp.sourceforge.dvibrowser.dvicore.api.SimpleMetrics;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+
+
+
+public class BasicGeometer
+extends DviObject
+implements Geometer
+{
+  private static final Logger LOGGER = Logger.getLogger(BasicGeometer.class.getName());
+  
+  public BasicGeometer(DviContextSupport dcs)
+  {
+    super(dcs);
+  }
+  
+  protected DevicePainter dp;
+  public void setPainter(DevicePainter dp) { this.dp = dp; }
+
+  private static final class GeometerContextImpl
+  implements GeometerContext
+  {
+    private DviFontSpec fs = null;
+    private SimpleMetrics sm = null;
+    private final Stack<DviRegister> stack = new Stack<DviRegister>();
+    private DviRegister reg;
+    private DviExecutorContext exe_ctx;
+
+    public DviFontSpec currentFontSpec() { return fs; }
+    public DviRegister getRegister() { return reg; }
+    public DviExecutorContext getExecuterContext() { return exe_ctx; }
+  }
+
+  private GeometerContextImpl ctx = null;
+  private DviFontTable ft = null;
+
+  public void begin(DviExecutorContext ctx)
+  throws DviException
+  {
+    this.ctx = new GeometerContextImpl();
+    this.ctx.reg = new DviRegister();
+    this.ctx.exe_ctx = ctx;
+    ft = ctx.getData().getFontTable();
+
+    dp.begin(this.ctx);
+  }
+
+  public void end()
+  throws DviException
+  {
+    if (ctx.fs != null) {
+      dp.endFont();
+    }
+    dp.end();
+    ctx = null;
+    ft = null;
+  }
+
+
+  public void doSet(int code)
+  throws DviException
+  {
+    dp.drawChar(code);
+
+    int tfmw = 0;
+    if (ctx.sm != null) {
+      tfmw = ctx.sm.getTfmWidth(code);
+    } else {
+    }
+    if (ctx.fs != null) {
+      ctx.reg.addH(ctx.fs.tfmToDvi(tfmw));
+    }
+  }
+
+  public void doSetRule(int width, int height)
+  throws DviException
+  {
+    dp.drawRule(width, height);
+    ctx.reg.addH(width);
+  }
+
+  public void doPut(int code) throws DviException {
+    dp.drawChar(code);
+  }
+
+  public void doPutRule(int w, int h) throws DviException {
+    dp.drawRule(w, h);
+  }
+
+  public void doNop() throws DviException {
+  }
+
+  public void doSelectFont(int fn) throws DviException {
+    if (ctx.fs != null)
+      dp.endFont();
+
+    ctx.fs = ft.get(fn);
+    ctx.sm = getDviContext().findDviSimpleMetrics(ctx.fs);
+    if (ctx.sm == null) {
+      LOGGER.fine("No metric for font number " + fn + " font spec: " + ctx.fs);
+    }
+    dp.beginFont(ctx.fs);
+  }
+  public void doDefineFont(int fn, DviFontSpec fs) throws DviException {
+    /* ignored. */
+  }
+
+  public void doPush() throws DviException {
+    ctx.stack.push((DviRegister) ctx.reg.clone());
+  }
+  public void doPop() throws DviException {
+    if (ctx.stack.empty())
+      throw new DviException("stack underflow.");
+
+    ctx.reg.copy(ctx.stack.pop());
+  }
+
+  public void doPre(DviPreamble preamble) throws DviException {
+  }
+  public void doBop(DviBop bop) throws DviException {
+    ctx.reg.reset();
+    ctx.stack.clear();
+    dp.beginPage(bop);
+  }
+  public void doEop() throws DviException {
+    dp.endPage();
+  }
+  public void doPost(DviPostamble postamble) throws DviException {
+  }
+  public void doPostPost(DviPostPost postPost) throws DviException {
+    ctx.exe_ctx.setTerminate(true);
+  }
+
+  public void doRight(int by) throws DviException {
+    ctx.reg.addH(by);
+  }
+  public void doW(int by) throws DviException {
+    ctx.reg.setW(by);
+    ctx.reg.addH(by);
+  }
+  public void doW0() throws DviException {
+    ctx.reg.addH(ctx.reg.getW());
+  }
+  public void doX(int by) throws DviException {
+    ctx.reg.setX(by);
+    ctx.reg.addH(by);
+  }
+  public void doX0() throws DviException {
+    ctx.reg.addH(ctx.reg.getX());
+  }
+
+  public void doDown(int by) throws DviException {
+    ctx.reg.addV(by);
+  }
+  public void doY(int by) throws DviException {
+    ctx.reg.setY(by);
+    ctx.reg.addV(by);
+  }
+  public void doY0() throws DviException {
+    ctx.reg.addV(ctx.reg.getY());
+  }
+  public void doZ(int by) throws DviException {
+    ctx.reg.setZ(by);
+    ctx.reg.addV(by);
+  }
+  
+  public void doZ0() throws DviException {
+    ctx.reg.addV(ctx.reg.getZ());
+  }
+
+  public void doSpecial(byte [] xxx) throws DviException {
+    dp.drawSpecial(xxx);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/BinaryImage.java b/src/jp/sourceforge/dvibrowser/dvicore/render/BinaryImage.java
new file mode 100644 (file)
index 0000000..e68fe7f
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+
+public class BinaryImage
+{
+  private final byte [] buf;
+  private final int pitch;
+  private final int width;
+  private final int height;
+
+  public BinaryImage(int width, int height)
+  {
+    this.width = width;
+    this.height = height;
+    this.pitch = (width + 7) >>> 3;
+    this.buf = new byte [pitch * height];
+  }
+
+  public BinaryImage(byte [] buf, int width, int height)
+  {
+    this.width = width;
+    this.height = height;
+    this.pitch = (width + 7) >>> 3;
+    this.buf = buf;
+    if (buf.length != pitch * height)
+      throw new IllegalArgumentException
+        ("buffer size mismatch.");
+  }
+
+  public byte [] getBuffer() { return buf; }
+
+  public int width() { return width; }
+  public int height() { return height; }
+  public int pitch() { return pitch; }
+
+  public void fill(int c)
+  {
+    for (int i=0; i<buf.length; i++)
+      buf[i] = (byte) c;
+  }
+
+  public int getPixel(int x, int y)
+  {
+    if (x < 0 || width <= x)
+      throw new ArrayIndexOutOfBoundsException
+        (String.valueOf(x));
+    if (y < 0 || height <= y)
+      throw new ArrayIndexOutOfBoundsException
+        (String.valueOf(y));
+
+    final int bytePos = (x >>> 3);
+    final byte bitMask = (byte)(1 << (7 - (x & 7)));
+    return (0 != (buf[bytePos + y * pitch] & bitMask)) ? 1 : 0;
+  }
+
+  public void setPixel(int x, int y, int val)
+  {
+    if (x < 0 || width <= x)
+      throw new ArrayIndexOutOfBoundsException
+        (String.valueOf(x));
+    if (y < 0 || height <= y)
+      throw new ArrayIndexOutOfBoundsException
+        (String.valueOf(y));
+
+    final int bytePos = (x >>> 3);
+    final int bitMask = 1 << (7 - (x & 7));
+    byte c = (byte)(buf[bytePos + y * pitch] & ((0xff) ^ bitMask));
+    if (val != 0) c |= bitMask;
+
+    buf[bytePos + y * pitch] = c;
+  }
+
+  public void dump()
+  {
+    for (int i=0; i<height; i++) {
+      StringBuilder sb = new StringBuilder();
+      for (int j=0; j<width; j++) {
+        if (0 != getPixel(j, i))
+          sb.append("*");
+        else 
+          sb.append(".");
+      }
+      System.out.println(sb.toString());
+    }
+  }
+
+
+
+  public BinaryDevice getBinaryDevice(DviResolution res)
+  {
+    return new BinaryDeviceImpl(res);
+  }
+
+  // TDOO: make this a static class
+  public final class BinaryDeviceImpl
+  extends AbstractDevice
+  implements BinaryDevice
+  {
+    private BinaryDeviceImpl(DviResolution res) {
+      super(res);
+    }
+
+    public DviRect getBounds() {
+      return new DviRect(-point.x, -point.y, width, height);
+    }
+
+    public void begin() {
+    }
+    public void end() {
+    }
+    
+    private int w;
+//    private int h;
+    private int y;
+    private byte [] l_buf;
+    private int xx;
+    public boolean beginRaster(int w, int h) {
+      this.w = w;
+//      this.h = h;
+      l_buf = new byte [(w + 7) >>> 3];
+      y = 0;
+      return true;
+    }
+    public void endRaster() {
+    }
+
+    public void beginLine() {
+      for (int i=0; i<l_buf.length; i++) l_buf[i] = 0;
+      xx = 0;
+    }
+    public void endLine(int repeat) {
+      for (int i=0; i<=repeat; i++) {
+        for (int j=0; j<w; j++) {
+          int bit = l_buf[(j >>> 3)] & (1 << (7 - (j & 7)));
+          if (bit != 0) {
+            setPixel(point.x + j, point.y + y, 1);
+          }
+        }
+        y++;
+      }
+    }
+  
+    public void putBits(int count, boolean paintFlag) {
+      if (paintFlag) {
+        while (count-- > 0) {
+          l_buf[(xx >>> 3)] |= 1 << (7 - (xx & 7));
+          xx++;
+        }
+      } else {
+        xx += count;
+      }
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/BoundingBoxComputer.java b/src/jp/sourceforge/dvibrowser/dvicore/render/BoundingBoxComputer.java
new file mode 100644 (file)
index 0000000..0239e38
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+
+public class BoundingBoxComputer
+extends AbstractDevice
+implements BinaryDevice
+{
+  private DviRect bbox = DviRect.EMPTY;
+  public BoundingBoxComputer(DviResolution res)
+  {
+    super(res);
+  }
+
+  public DviRect getBoundingBox()
+  throws DviException
+  {
+    return bbox;
+  }
+
+  public void begin()
+  throws DviException
+  {
+  }
+
+  public void end()
+  throws DviException
+  {
+  }
+
+  public boolean beginRaster(int w, int h)
+  throws DviException
+  {
+    bbox = bbox.union(new DviRect(getReferencePoint(), w, h));
+    return false;
+  }
+
+  public void endRaster()
+  throws DviException
+  {
+  }
+
+  public void beginLine()
+  throws DviException
+  {
+    // not called.
+  }
+
+  public void endLine(int repeat)
+  throws DviException
+  {
+    // not called.
+  }
+
+  public void putBits(int count, boolean paintFlag)
+  throws DviException
+  {
+    // not called.
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/ByteRGBImage.java b/src/jp/sourceforge/dvibrowser/dvicore/render/ByteRGBImage.java
new file mode 100644 (file)
index 0000000..df2ee31
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.ImageDevice;
+
+// TODO: use GammaCorrector.
+// TODO: test this class
+
+public class ByteRGBImage
+{
+  private final byte [] buf;
+  private final int width;
+  private final int height;
+  private final int pitch;
+
+  public ByteRGBImage(int width, int height)
+  {
+    this.width = width;
+    this.height = height;
+    this.pitch = width * 3;
+    this.buf = new byte [pitch * height];
+  }
+
+  public ByteRGBImage(byte [] buf, int width, int height, int pitch)
+  {
+    this.buf = buf;
+    this.width = width;
+    this.height = height;
+    this.pitch = pitch;
+  }
+
+  public ByteRGBImage(byte [] buf, int width, int height)
+  {
+    this(buf, width, height, 3*width);
+  }
+
+  public byte [] getBuffer() { return buf; }
+
+  public int width() { return width; }
+  public int height() { return height; }
+
+  public void fill(int rgb)
+  {
+    final byte r = (byte) (rgb >>> 16);
+    final byte g = (byte) (rgb >>>  8);
+    final byte b = (byte) (rgb >>>  0);
+    int p0 = 0;
+    for (int i=0; i<height; i++) {
+      int p = p0;
+      for (int j=0; j<width; j++) {
+        buf[p++] = r;
+        buf[p++] = g;
+        buf[p++] = b;
+      }
+      p0 += pitch;
+    }
+  }
+
+  public ImageDevice getImageDevice(DviResolution res)
+  {
+    return new ImageDeviceImpl(res);
+  }
+
+  // TODO: make this static
+  private class ImageDeviceImpl
+  extends AbstractDevice
+  implements ImageDevice
+  {
+    protected ImageDeviceImpl(DviResolution res) {
+      super(res);
+    }
+
+    public DviRect getBounds() {
+      return new DviRect(-point.x, -point.y, width, height);
+    }
+
+    private final AlphaCache alphaCache = new AlphaCache();
+    public void begin(int maxval) {
+      alphaCache.setMaxValue(maxval);
+    }
+    public void end() {
+    }
+
+    private int ptr = 0;
+//    private int gw;
+//    private int gh;
+    public boolean beginImage(int w, int h)
+    throws DviException
+    {
+//      gw = w;
+//      gh = h;
+      ptr = 3 * point.x + point.y * pitch;
+      return true;
+    }
+    public void endImage() {
+    }
+
+    public void putLine(int [] l_buf, int off, int len)
+    throws DviException
+    {
+      final int color = getColor().toIntRGB();
+      final byte r = (byte) (color >>> 16);
+      final byte g = (byte) (color >>>  8);
+      final byte b = (byte) (color >>>  0);
+      int p = ptr;
+      for (int i=0; i<len; i++) {
+        final int alpha10 = alphaCache.get(l_buf[off + i]);
+        if (alpha10 != 0) {
+          buf[p+0] = blend(buf[p+0], r, alpha10);
+          buf[p+1] = blend(buf[p+1], g, alpha10);
+          buf[p+2] = blend(buf[p+2], b, alpha10);
+        }
+        p += 3;
+      }
+      ptr += pitch;
+    }
+  }
+
+  private static class AlphaCache
+  {
+    private final int [] table = new int[1024];
+    private int maxval;
+    private boolean canLookUp;
+    private double gamma = 0.90;
+    private AlphaCache() {}
+
+    /*
+    private void setGamma(double gamma) {
+      this.gamma = Math.max(0.0, gamma);
+    }
+    */
+
+    private void setMaxValue(int maxval) {
+      if (this.maxval == maxval) return;
+      this.maxval = maxval;
+      if (maxval < 1024) {
+        for (int i=0; i<=maxval; i++)
+          table[i] = alpha10Internal(i);
+        canLookUp = true;
+      } else {
+        canLookUp = false;
+      }
+    }
+
+    private int get(int c) {
+      if (!canLookUp || c < 0 || maxval < c)
+        return alpha10Internal(c);
+      else 
+        return table[c];
+    }
+
+    private int alpha10Internal(int c) {
+      return (int) (1024 * Math.pow((double) c / maxval, gamma) + 0.5);
+    }
+  }
+
+  private static byte blend(final byte _c1, final byte _c2, final int alpha10)
+  {
+    final int c1 = ((int) _c1) & 0xff;
+    final int c2 = ((int) _c2) & 0xff;
+    return (byte) (c1 + ((alpha10 * (c2 - c1)) >>> 10));
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/ByteRangeComputer.java b/src/jp/sourceforge/dvibrowser/dvicore/render/ByteRangeComputer.java
new file mode 100644 (file)
index 0000000..c7e8399
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviByteRange;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+import jp.sourceforge.dvibrowser.dvicore.api.Glyph;
+import jp.sourceforge.dvibrowser.dvicore.font.LogicalFont;
+
+public class ByteRangeComputer
+extends DefaultDevicePainter
+{
+  private DviByteRange range = DviByteRange.EMPTY;
+  private final DviRect rect;
+  private final BinaryDevice out;
+  private DviRect bbox = DviRect.EMPTY;
+  private DviResolution res;
+
+  public ByteRangeComputer(DviContextSupport dcs, DviResolution res, DviRect rect)
+  throws DviException
+  {
+    super(dcs);
+    this.rect = rect;
+    this.res = res;
+    out = new EmptyBinaryDevice(res);
+    setOutput(out);
+  }
+
+  public DviByteRange getByteRange()
+  {
+    return range;
+  }
+
+  public DviRect getBounds()
+  {
+    return bbox;
+  }
+
+  protected void realDrawChar(LogicalFont lf, int code)
+  throws DviException
+  {
+    DviFont font = getFont();
+    if (font != null) {
+      Glyph glyph = font.getGlyph(lf, code);
+      if (glyph != null) {
+        shipOutBOX(
+          glyph.bounds().translate(out.getReferencePoint())
+        );
+      }
+    }
+  }
+
+  protected void realDrawRule(int w, int h)
+  throws DviException
+  {
+    shipOutBOX(
+      new DviRect(0, 0, w, h).translate(out.getReferencePoint())
+    );
+  }
+
+  private void shipOutBOX(DviRect box)
+  throws DviException
+  {
+    if (box.intersects(rect)) {
+      bbox = bbox.union(box);
+      DviExecutorContext ctx = getGeometerContext().getExecuterContext();
+      range = range.union(
+        ctx.getCommandRange()
+      );
+    }
+  }
+
+  public void drawSpecial(byte [] _xxx)
+  throws DviException
+  {
+    // ignored.
+  }
+
+  public DviResolution getResolution() {
+       return res;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/DefaultDevicePainter.java b/src/jp/sourceforge/dvibrowser/dvicore/render/DefaultDevicePainter.java
new file mode 100644 (file)
index 0000000..ce721ac
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviByteRange;
+import jp.sourceforge.dvibrowser.dvicore.DviColor;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviRegister;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+import jp.sourceforge.dvibrowser.dvicore.api.DevicePainter;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviData;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.api.FullMetrics;
+import jp.sourceforge.dvibrowser.dvicore.api.GeometerContext;
+import jp.sourceforge.dvibrowser.dvicore.api.Glyph;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+import jp.sourceforge.dvibrowser.dvicore.ctx.DviToolkit;
+import jp.sourceforge.dvibrowser.dvicore.font.LogicalFont;
+import jp.sourceforge.dvibrowser.dvicore.special.Anchor;
+import jp.sourceforge.dvibrowser.dvicore.special.AnchorSet;
+
+
+// TODO: support color special across pages.
+
+public class DefaultDevicePainter
+extends DviObject
+implements DevicePainter
+{
+  private static final Logger LOGGER = Logger.getLogger(DefaultDevicePainter.class.getName());
+  private BinaryDevice out = null;
+  private DviColor defaultColor;
+  
+  public DefaultDevicePainter(DviContextSupport dcs)
+  {
+    super(dcs);
+  }
+
+  public void setOutput(BinaryDevice out)
+  throws DviException
+  {
+    this.out = out;
+    defaultColor = out.getColor();
+  }
+
+  public BinaryDevice getOutput() { return out; }
+
+
+  private GeometerContext geom_ctx = null;
+
+  protected GeometerContext getGeometerContext()
+  {
+    return geom_ctx;
+  }
+
+  private DviUnit dviUnit = null;
+
+  protected DviUnit getDviUnit()
+  {
+    return dviUnit;
+  }
+
+  private DviResolution res = null;
+  private double factor;
+
+  public void begin(GeometerContext ctx)
+  throws DviException
+  {
+    geom_ctx = ctx;
+    dviUnit = geom_ctx.getExecuterContext()
+                      .getData()
+                      .getDviUnit();
+    res = out.getResolution();
+    out.begin();
+    out.translate(res.dpi(), res.dpi());
+    factor = dviUnit.factorDouble(res.dpi());
+  }
+
+  public void end()
+  throws DviException
+  {
+    out.end();
+    res = null;
+    dviUnit = null;
+    geom_ctx = null;
+  }
+
+  private AnchorSet anchors = null;
+
+  public void beginPage(DviBop bop)
+  throws DviException
+  {
+    DviToolkit utils = getDviContext().getDviToolkit();
+    DviData data = geom_ctx.getExecuterContext().getData();
+    if (data instanceof DviPage) {
+      anchors = utils.getAnchorSet((DviPage) data);
+      LOGGER.finer("anchors=" + anchors);
+    }
+  }
+
+  public void endPage()
+  throws DviException
+  {
+    anchors = null;
+  }
+
+  private LogicalFont lf = null;
+
+  protected LogicalFont getLogicalFont()
+  {
+    return lf;
+  }
+
+  private boolean fontResolved = false;
+  private DviFont font = null;
+
+  protected DviFont getFont()
+  {
+    return font;
+  }
+
+  public void beginFont(DviFontSpec fs)
+  throws DviException
+  {
+    lf = LogicalFont.getInstance(fs, dviUnit, res);
+    fontResolved = false;
+    font = null;
+  }
+
+  public void endFont()
+  throws DviException
+  {
+    fontResolved = false;
+    font = null;
+    lf = null;
+  }
+
+  private boolean enableCharRendering = true;
+  private boolean enableCharBoundingBox = false;
+  
+  private void drawRectInternal(int ax, int ay, int ex, int ey)
+  throws DviException
+  {
+    out.save();
+    try {
+      out.translate(ax, ay);
+      realDrawRule(ex - ax + 1, ey - ay + 1);
+    } finally {
+      out.restore();
+    }
+  }
+  
+  protected void drawCharBoundingBox(int code)
+  throws DviException
+  {
+    final DviRegister reg = geom_ctx.getRegister();
+
+    DviFontSpec fs = lf.fontSpec();
+    FullMetrics fm = getDviContext().findDviFullMetrics(fs);
+    if (fm != null) {
+      int width = fs.tfmToDvi(fm.getTfmWidth(code));
+      int height = fs.tfmToDvi(fm.getTfmHeight(code));
+      int depth = fs.tfmToDvi(fm.getTfmDepth(code));
+
+      final int ax_sf = (int)(factor * reg.getH() + 0.5);
+      final int ex_sf = (int)(factor * (reg.getH() + width - 1) + 0.5);
+      final int ay_sf = (int)(factor * (reg.getV() - height + 1) + 0.5);
+      final int ey_sf = (int)(factor * (reg.getV() + depth - 1) + 0.5);
+      final int by_sf = (int)(factor * (reg.getV() ) + 0.5);
+      
+      drawRectInternal(ax_sf, ay_sf, ex_sf, ay_sf);
+      drawRectInternal(ax_sf, ey_sf, ex_sf, ey_sf);
+      drawRectInternal(ax_sf, ay_sf, ax_sf, ey_sf);
+      drawRectInternal(ex_sf, ay_sf, ex_sf, ey_sf);
+      drawRectInternal(ax_sf, by_sf, ex_sf, by_sf);
+    }
+  }
+  
+  public void drawChar(int code)
+  throws DviException
+  {
+    handleAnchors();
+
+    if (getEnableCharRendering()) {
+      final DviRegister reg = geom_ctx.getRegister();
+      final int rx_sf = (int) (factor * reg.getH() + 0.5);
+      final int ry_sf = (int) (factor * reg.getV() + 0.5);
+
+      out.save();
+      try {
+        out.translate(rx_sf, ry_sf);
+        realDrawChar(lf, code);
+      } finally {
+        out.restore();
+      }
+    }
+    if (getEnableCharBoundingBox()) {
+      drawCharBoundingBox(code);
+    }
+  }
+  
+  protected void resolveFont()
+  {
+    if (!fontResolved) {
+      try {
+        font = getDviContext().findDviFont(lf);
+      } catch (DviException ex) {
+        // TODO: logging
+        ex.printStackTrace();
+        font = null;
+      } finally {
+        fontResolved = true;
+      }
+    }
+  }
+  
+  protected void realDrawChar(LogicalFont lf, int code)
+  throws DviException
+  {
+    resolveFont();
+    if (font == null)
+      return;
+
+    determineColor(out);
+    Glyph g = font.getGlyph(lf, code);
+
+    if (g != null) {
+      g.rasterizeTo(out);
+    }
+  }
+
+  public void drawRule(int width, int height)
+  throws DviException
+  {
+    handleAnchors();
+    if (width <= 0 || height <= 0) return;
+
+    final DviRegister reg = geom_ctx.getRegister();
+
+    final int ax_sf = (int)(factor * reg.getH() + 0.5);
+    final int ex_sf = (int)(factor * (reg.getH() + width - 1) + 0.5);
+    final int ay_sf = (int)(factor * (reg.getV() - height + 1) + 0.5);
+    final int ey_sf = (int)(factor * reg.getV() + 0.5);
+
+    out.save();
+    try {
+      out.translate(ax_sf, ay_sf);
+      realDrawRule(ex_sf - ax_sf + 1, ey_sf - ay_sf + 1);
+    } finally {
+      out.restore();
+    }
+  }
+
+  protected void realDrawRule(int w_sf, int h_sf)
+  throws DviException
+  {
+    if (out.beginRaster(w_sf, h_sf)) {
+      determineColor(out);
+      out.beginLine();
+      out.putBits(w_sf, true);
+      out.endLine(h_sf);
+    }
+    out.endRaster();
+  }
+
+  private final java.util.Stack<DviColor> colorStack
+    = new java.util.Stack<DviColor>();
+  private static final java.util.regex.Pattern pushPat
+    = java.util.regex.Pattern.compile(
+      "color\\s+push\\s+(.*)",
+      java.util.regex.Pattern.CASE_INSENSITIVE
+    );
+  private static final java.util.regex.Pattern popPat
+    = java.util.regex.Pattern.compile(
+      "color\\s+pop",
+      java.util.regex.Pattern.CASE_INSENSITIVE
+    );
+  private static final java.util.regex.Pattern colorPat
+    = java.util.regex.Pattern.compile(
+      "color\\s+(.*)",
+      java.util.regex.Pattern.CASE_INSENSITIVE
+    );
+  private DviColor specialColor = DviColor.INVALID;
+
+  
+  // TODO: outsource color special handler.
+  public void drawSpecial(byte [] _xxx)
+  throws DviException
+  {
+    handleAnchors();
+    String xxx = new String(_xxx);
+    java.util.regex.Matcher mat;
+    if ((mat = pushPat.matcher(xxx)).matches()) {
+      DviColor aColor = DviColor.parseColor(mat.group(1));
+      colorStack.push(out.getColor());
+      specialColor = aColor;
+    } else if ((mat = popPat.matcher(xxx)).matches()) {
+      if (colorStack.empty())
+        // TODO: Handle this as a warning.
+        throw new IllegalStateException
+          ("Color stack underflow");
+      specialColor = colorStack.pop();
+    } else if ((mat = colorPat.matcher(xxx)).matches()) {
+      DviColor aColor = DviColor.parseColor(mat.group(1));
+      specialColor = aColor;
+    }
+  }
+
+  // TODO: make anchor renderer injectable.
+  private DviColor anchorColor = DviColor.INVALID;
+  private void handleAnchors()
+  throws DviException
+  {
+    if (anchors == null || anchors.size() == 0) return;
+    LOGGER.finer("Rendering Anchor" + anchors);
+    long begin = geom_ctx.getExecuterContext().getCommandRange().begin();
+    anchorColor = DviColor.INVALID;
+    for (DviByteRange br : anchors) {
+      if (!br.contains(begin)) continue;
+      if (br instanceof Anchor.Href) {
+  //      Anchor.Href href = (Anchor.Href) br;
+        //TODO: outsource the color settings.
+        anchorColor = DviColor.parseColor("blue");
+//      } else if (br instanceof Anchor.Source) {
+//        Anchor.Source source = (Anchor.Source) br;
+        // anchorColor = DviColor.parseColor("yellow");
+      }
+    }
+  }
+
+  private void determineColor(BinaryDevice out) 
+  throws DviException
+  {
+    if (anchorColor.isValid()) {
+      out.setColor(anchorColor);
+    } else if (specialColor.isValid()) {
+      out.setColor(specialColor);
+    } else {
+      out.setColor(defaultColor);
+    }
+  }
+
+  public void setEnableCharRendering(boolean enableCharRendering)
+  {
+    this.enableCharRendering = enableCharRendering;
+  }
+
+  public boolean getEnableCharRendering()
+  {
+    return enableCharRendering;
+  }
+
+  public void setEnableCharBoundingBox(boolean enableCharBoundingBox)
+  {
+    this.enableCharBoundingBox = enableCharBoundingBox;
+  }
+
+  public boolean getEnableCharBoundingBox()
+  {
+    return enableCharBoundingBox;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/DefaultGammaCorrector.java b/src/jp/sourceforge/dvibrowser/dvicore/render/DefaultGammaCorrector.java
new file mode 100644 (file)
index 0000000..8f889bf
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import jp.sourceforge.dvibrowser.dvicore.api.GammaCorrector;
+
+
+public class DefaultGammaCorrector
+implements GammaCorrector
+{
+  private static final AtomicLong serializer = new AtomicLong();
+  private final double factor, exponent;
+  private final long serial;
+
+  public DefaultGammaCorrector()
+  {
+    this(1.0, 1.0);
+  }
+  
+  public DefaultGammaCorrector(double factor, double exponent)
+  {
+    this.factor = factor;
+    this.exponent = exponent;
+    this.serial = serializer.incrementAndGet();
+  }
+
+  public double factor() { return factor; }
+  public double exponent() { return exponent; }
+
+  public int correctGamma(int c, int maxval)
+  {
+    return (int) (
+        1024 *
+          Math.min(
+            1.0,
+            Math.pow(factor * c / maxval, exponent)
+          )
+        + 0.5
+      );
+  }
+  
+  @Override
+  public int hashCode() {
+    return (int) (33 * (factor + 33 * exponent));
+  }
+  
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof DefaultGammaCorrector)) return false;
+    DefaultGammaCorrector gc = (DefaultGammaCorrector) o;
+    return (factor == gc.factor && exponent == gc.exponent);
+  }
+  
+  public String getCacheKey() {
+    return getClass().getName() + "--" + serial;
+  }
+  
+  @Override
+  public String toString() {
+    return getClass().getName() + "[factor=" + factor+ ",exponent=" + exponent + "]";
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/DumpBinaryDevice.java b/src/jp/sourceforge/dvibrowser/dvicore/render/DumpBinaryDevice.java
new file mode 100644 (file)
index 0000000..8e77324
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import java.io.PrintStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+
+
+public class DumpBinaryDevice
+extends AbstractDevice
+implements BinaryDevice
+{
+  private final PrintStream out;
+  
+  // TODO: outsource the default resolution
+  public DumpBinaryDevice(PrintStream out)
+  {
+    super(new DviResolution(1200, 10));
+    this.out = out;
+  }
+
+  public void begin() throws DviException {}
+  public void end() throws DviException {}
+
+  public boolean beginRaster(int gw_sf, int gh_sf)
+  throws DviException
+  {
+    return true;
+  }
+
+  public void endRaster()
+  throws DviException
+  {
+    out.println();
+  }
+
+  String buf;
+
+  public void beginLine()
+  throws DviException
+  {
+    buf = "";
+  }
+
+  public void endLine(int repeat)
+  throws DviException
+  {
+    for (int i=0; i<=repeat; i++) {
+      out.println(buf);
+    }
+  }
+
+  public void putBits(int count, boolean paintFlag)
+  throws DviException
+  {
+    for (int i=0; i<count; i++) {
+      if (paintFlag) {
+        buf += "*";
+      } else {
+        buf += ".";
+      }
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/DumpHandler.java b/src/jp/sourceforge/dvibrowser/dvicore/render/DumpHandler.java
new file mode 100644 (file)
index 0000000..5de2129
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import java.io.PrintStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorHandler;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviCommand;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+
+
+
+public class DumpHandler
+implements DviExecutorHandler
+{
+  private DviExecutorContext ctx;
+
+  private final PrintStream out;
+  public DumpHandler(PrintStream out)
+  {
+    this.out = out;
+    if (out == null)
+      throw new NullPointerException("output cannot be null");
+  }
+  
+  public void begin(DviExecutorContext ctx) throws DviException {
+    this.ctx = ctx;
+  }
+  public void end() throws DviException {
+    ctx = null;
+  }
+
+  public void doSet(int code) throws DviException {
+    dump("code=" + code);
+  }
+  public void doSetRule(int w, int h) throws DviException {
+    dump("set_rule: w=" + w + " h=" + h);
+  }
+  public void doPut(int code) throws DviException {
+    dump("code=" + code);
+  }
+  public void doPutRule(int w, int h) throws DviException {
+    dump("put_rule: w=" + w + " h=" + h);
+  }
+  public void doNop() throws DviException {
+    dump("");
+  }
+
+  public void doSelectFont(int fn) throws DviException {
+    dump(": fn=" + fn);
+  }
+  public void doDefineFont(int fn, DviFontSpec fs) throws DviException {
+    dump(": fn=" + fn + " fs=" + fs);
+  }
+
+  public void doPush() throws DviException {
+    dump("");
+  }
+  public void doPop() throws DviException {
+    dump("");
+  }
+
+  public void doPre(DviPreamble preamble) throws DviException {
+    dump(preamble.toString());
+  }
+  public void doBop(DviBop bop) throws DviException {
+    dump(bop.toString());
+  }
+  public void doEop() throws DviException {
+    dump("");
+  }
+  public void doPost(DviPostamble postamble) throws DviException {
+    dump(postamble.toString());
+  }
+  public void doPostPost(DviPostPost postPost) throws DviException {
+    dump(postPost.toString());
+  }
+
+  public void doRight(int by) throws DviException {
+    dump("dh=" + by);
+  }
+  public void doW(int by) throws DviException {
+    dump("dh=" + by);
+  }
+  public void doW0() throws DviException {
+    dump("");
+  }
+  public void doX(int by) throws DviException {
+    dump("dh=" + by);
+  }
+  public void doX0() throws DviException {}
+
+  public void doDown(int by) throws DviException {
+    dump("dv=" + by);
+  }
+  public void doY(int by) throws DviException {
+    dump("dv=" + by);
+  }
+  public void doY0() throws DviException {
+    dump("");
+  }
+  public void doZ(int by) throws DviException {
+    dump("dv=" + by);
+  }
+  public void doZ0() throws DviException {
+    dump("");
+  }
+
+  public void doSpecial(byte [] xxx) throws DviException {
+    dump(new String(xxx));
+  }
+
+  private void dump(String msg) {
+    out.println(
+      ctx.getCommandRange().toString()
+      + ": " + DviCommand.getName(ctx.getCommand())
+      + ": " + msg
+    );
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/DviBoundingBoxPreparator.java b/src/jp/sourceforge/dvibrowser/dvicore/render/DviBoundingBoxPreparator.java
new file mode 100644 (file)
index 0000000..cf57695
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.api.Geometer;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.Computation;
+
+
+public class DviBoundingBoxPreparator
+extends DviObject
+implements Computation<String, DviRect> {
+  private static final Logger LOGGER = Logger
+      .getLogger(DviBoundingBoxPreparator.class.getName());
+  private final DviPage page;
+  private final DviResolution res;
+
+  public DviBoundingBoxPreparator(DviContextSupport dcs, DviPage page, DviResolution res) {
+    super(dcs);
+    this.page = page;
+    this.res = res;
+  }
+  
+  public DviRect call() throws Exception
+  {
+    BoundingBoxComputer bbc = new BoundingBoxComputer(res);
+    DefaultDevicePainter dp = new DefaultDevicePainter(this);
+    dp.setEnableCharBoundingBox(true);
+    dp.setEnableCharRendering(false);
+    dp.setOutput(bbc);
+    Geometer geometer = new BasicGeometer(this);
+    geometer.setPainter(dp);
+    getDviContext().execute(page, geometer);
+    DviRect bbox = bbc.getBoundingBox();
+    LOGGER.finer("bounding box=" + bbox + " resolution=" + res + " page=" + page);
+    return bbox;
+  }
+  
+  public String getCacheKey() {
+    String key = page.getCacheKey() + "-" + res.dpi();
+    return key;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/DviExecutorFilter.java b/src/jp/sourceforge/dvibrowser/dvicore/render/DviExecutorFilter.java
new file mode 100644 (file)
index 0000000..6859d52
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorHandler;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+
+public class DviExecutorFilter
+implements DviExecutorHandler
+{
+  private static final EmptyDviExecutorHandler empty = new EmptyDviExecutorHandler();
+  private final DviExecutorHandler next;
+
+  public DviExecutorFilter(DviExecutorHandler next)
+  {
+    this.next = (next != null) ? next : empty;
+  }
+
+  public void begin(DviExecutorContext ctx)
+  throws DviException
+  {
+    next.begin(ctx);
+  }
+
+  public void end()
+  throws DviException
+  {
+    next.end();
+  }
+
+  public void doSet(int code)
+  throws DviException
+  {
+    next.doSet(code);
+  }
+
+  public void doSetRule(int w, int h)
+  throws DviException
+  {
+    next.doSetRule(w, h);
+  }
+
+  public void doPut(int code)
+  throws DviException
+  {
+    next.doPut(code);
+  }
+
+  public void doPutRule(int w, int h)
+  throws DviException
+  {
+    next.doPutRule(w, h);
+  }
+
+  public void doNop()
+  throws DviException
+  {
+    next.doNop();
+  }
+
+  public void doSelectFont(int fn)
+  throws DviException
+  {
+    next.doSelectFont(fn);
+  }
+
+  public void doDefineFont(int fn, DviFontSpec fs)
+  throws DviException
+  {
+    next.doDefineFont(fn, fs);
+  }
+
+  public void doPush()
+  throws DviException
+  {
+    next.doPush();
+  }
+
+  public void doPop()
+  throws DviException
+  {
+    next.doPop();
+  }
+
+  public void doPre(DviPreamble preamble)
+  throws DviException
+  {
+    next.doPre(preamble);
+  }
+
+  public void doBop(DviBop bop)
+  throws DviException
+  {
+    next.doBop(bop);
+  }
+
+  public void doEop()
+  throws DviException
+  {
+    next.doEop();
+  }
+
+  public void doPost(DviPostamble postamble)
+  throws DviException
+  {
+    next.doPost(postamble);
+  }
+
+  public void doPostPost(DviPostPost postPost)
+  throws DviException
+  {
+    next.doPostPost(postPost);
+  }
+
+  public void doRight(int by)
+  throws DviException
+  {
+    next.doRight(by);
+  }
+
+  public void doW(int by)
+  throws DviException
+  {
+    next.doW(by);
+  }
+
+  public void doW0()
+  throws DviException
+  {
+    next.doW0();
+  }
+
+  public void doX(int by)
+  throws DviException
+  {
+    next.doX(by);
+  }
+
+  public void doX0()
+  throws DviException
+  {
+    next.doX0();
+  }
+
+  public void doDown(int by)
+  throws DviException
+  {
+    next.doDown(by);
+  }
+
+  public void doY(int by)
+  throws DviException
+  {
+    next.doY(by);
+  }
+
+  public void doY0()
+  throws DviException
+  {
+    next.doY0();
+  } 
+
+  public void doZ(int by)
+  throws DviException
+  {
+    next.doZ(by);
+  }
+
+  public void doZ0()
+  throws DviException
+  {
+    next.doZ0();
+  }
+
+  public void doSpecial(byte [] xxx)
+  throws DviException
+  {
+    next.doSpecial(xxx);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/DviPagePreparator.java b/src/jp/sourceforge/dvibrowser/dvicore/render/DviPagePreparator.java
new file mode 100644 (file)
index 0000000..cbb553d
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.ctx.DviToolkit;
+import jp.sourceforge.dvibrowser.dvicore.gui.swing.ViewSpec;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.Computation;
+import jp.sourceforge.dvibrowser.dvicore.util.progress.ProgressItem;
+
+
+public class DviPagePreparator
+extends DviObject
+implements Computation<String, Long> {
+  private static final Logger LOGGER = Logger.getLogger(DviPagePreparator.class
+      .getName());
+  private final DviPage page;
+  private final ViewSpec viewSpec;
+  
+  public DviPagePreparator(DviContextSupport dcs, DviPage page, ViewSpec viewSpec)
+  {
+    super(dcs);
+    this.page = page;
+    this.viewSpec = viewSpec;
+  }
+
+  public Long call() throws Exception
+  {
+    if (page == null) return -1L;
+    final ProgressItem progress = getDviContext().getProgressRecorder().open("loading page " + (page.getPageNumber() + 1));
+    try {
+      long start = System.currentTimeMillis();
+      LOGGER.finer("Start preparation of page " + page);
+      ViewSpec dummyViewSpec = (ViewSpec) viewSpec.clone();
+      DviResolution res = viewSpec.getResolution();
+      dummyViewSpec.setResolution(res.approximate(10));
+      DviToolkit utils = getDviContext().getDviToolkit();
+      utils.renderToBufferedImage(page, null, dummyViewSpec);
+      long end = System.currentTimeMillis();
+      LOGGER.finer("Finished preparation of page " + page);
+      LOGGER.finer("  Elapsed time = " + (end - start));
+      return (end - start);
+    } finally {
+      progress.close();
+    }
+  }
+
+  public String getCacheKey()
+  {
+    return page.getCacheKey();
+  }
+
+  public DviPage getPage()
+  {
+    return page;
+  }
+
+  public ViewSpec getViewSpec()
+  {
+    return viewSpec;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/EmptyBinaryDevice.java b/src/jp/sourceforge/dvibrowser/dvicore/render/EmptyBinaryDevice.java
new file mode 100644 (file)
index 0000000..519a198
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+
+public class EmptyBinaryDevice
+extends AbstractDevice
+implements BinaryDevice
+{
+  public EmptyBinaryDevice(DviResolution res)
+  {
+    super(res);
+  }
+
+  public void begin() throws DviException {}
+  public void end() throws DviException {}
+
+  public boolean beginRaster(int gw_sf, int gh_sf)
+  throws DviException
+  {
+    return true;
+  }
+  public void endRaster() throws DviException {}
+  public void beginLine() throws DviException {}
+  public void endLine(int repeat) throws DviException {}
+  public void putBits(int count, boolean paintFlag) throws DviException {}
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/EmptyDevicePainter.java b/src/jp/sourceforge/dvibrowser/dvicore/render/EmptyDevicePainter.java
new file mode 100644 (file)
index 0000000..ab8e26f
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+import jp.sourceforge.dvibrowser.dvicore.api.DevicePainter;
+import jp.sourceforge.dvibrowser.dvicore.api.GeometerContext;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+
+public class EmptyDevicePainter
+implements DevicePainter
+{
+  public void setOutput(BinaryDevice out) throws DviException {}
+
+  public void begin(GeometerContext ctx) throws DviException {}
+  public void end() throws DviException {}
+  public void beginPage(DviBop bop) throws DviException {}
+  public void endPage() throws DviException {}
+
+  public void beginFont(DviFontSpec fs) throws DviException {}
+  public void endFont() throws DviException {}
+
+  public void drawChar(int code) throws DviException {}
+  public void drawRule(int width, int height) throws DviException {}
+  public void drawSpecial(byte [] xxx) throws DviException {}
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/EmptyDviExecutorHandler.java b/src/jp/sourceforge/dvibrowser/dvicore/render/EmptyDviExecutorHandler.java
new file mode 100644 (file)
index 0000000..c79ce40
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorHandler;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+
+public class EmptyDviExecutorHandler
+implements DviExecutorHandler
+{
+  public void begin(DviExecutorContext ctx) throws DviException {}
+  public void end() throws DviException {}
+
+  public void doSet(int code) throws DviException {}
+  public void doSetRule(int w, int h) throws DviException {}
+  public void doPut(int code) throws DviException {}
+  public void doPutRule(int w, int h) throws DviException {}
+  public void doNop() throws DviException {}
+
+  public void doSelectFont(int fn) throws DviException {}
+  public void doDefineFont(int fn, DviFontSpec fs) throws DviException {}
+
+  public void doPush() throws DviException {}
+  public void doPop() throws DviException {}
+
+  public void doPre(DviPreamble preamble) throws DviException {}
+  public void doBop(DviBop bop) throws DviException {}
+  public void doEop() throws DviException {}
+  public void doPost(DviPostamble postamble) throws DviException {}
+  public void doPostPost(DviPostPost postPost) throws DviException {}
+
+  public void doRight(int by) throws DviException {}
+  public void doW(int by) throws DviException {}
+  public void doW0() throws DviException {}
+  public void doX(int by) throws DviException {}
+  public void doX0() throws DviException {}
+
+  public void doDown(int by) throws DviException {}
+  public void doY(int by) throws DviException {}
+  public void doY0() throws DviException {}
+  public void doZ(int by) throws DviException {}
+  public void doZ0() throws DviException {}
+
+  public void doSpecial(byte [] xxx) throws DviException {}
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/GammaCorrectorCache.java b/src/jp/sourceforge/dvibrowser/dvicore/render/GammaCorrectorCache.java
new file mode 100644 (file)
index 0000000..7dbe436
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.api.GammaCorrector;
+import jp.sourceforge.dvibrowser.dvicore.util.DviCache;
+
+public class GammaCorrectorCache
+implements GammaCorrector
+{
+  private static final int MAX_LOOKUP_TABLE_SIZE = 65536;
+  private int maxval;
+  private LookupTable table;
+  private boolean canLookUp;
+  private final GammaCorrector gammaCorrector;
+  
+  private static class LookupTable
+  {
+    private final int[] data;
+
+    public LookupTable(int maxval)
+    {
+      this.data = new int [maxval+1];
+    }
+  }
+  
+  private static final DviCache<String, LookupTable>
+    cache = new DviCache<String, LookupTable>(10);
+  public static GammaCorrector wrap(GammaCorrector gammaCorrector)
+  {
+    return new GammaCorrectorCache(gammaCorrector);
+  }
+  
+  private GammaCorrectorCache(GammaCorrector gammaCorrector)
+  {
+    this.gammaCorrector = gammaCorrector;
+  }
+  
+  public void setMaxValue(int maxval)
+  {
+    this.maxval = maxval;
+    if (maxval < MAX_LOOKUP_TABLE_SIZE) {
+      String key = gammaCorrector.getCacheKey() + "--" + maxval;
+      LookupTable table = cache.get(key);
+      if (table == null) {
+        table = new LookupTable(maxval);
+        for (int i=0; i<=maxval; i++) {
+          table.data[i] = doCorrectGamma(i, maxval);
+        }
+        cache.put(key, table);
+      }
+      this.table = table;
+      canLookUp = true;
+    } else {
+      canLookUp = false;
+    }
+  }
+
+  public int correctGamma(int c, int maxval)
+  {
+    if (c < 0) throw new IllegalArgumentException("c is negative");
+    if (maxval < 0) throw new IllegalArgumentException("maxval is negative");
+    
+    if (gammaCorrector == null) return 0;
+    if (maxval != this.maxval) setMaxValue(maxval);
+    if (canLookUp && c <= maxval) {
+      return table.data[c];
+    }
+    return doCorrectGamma(c, maxval);
+  }
+  
+  private int doCorrectGamma(int c, int maxval)
+  {
+    return gammaCorrector.correctGamma(c, maxval);
+  }
+
+  public GammaCorrector getGammaCorrector()
+  {
+    return gammaCorrector;
+  }
+
+  public String getCacheKey() {
+    return null;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/IntRGBImage.java b/src/jp/sourceforge/dvibrowser/dvicore/render/IntRGBImage.java
new file mode 100644 (file)
index 0000000..9288c26
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.GammaCorrector;
+import jp.sourceforge.dvibrowser.dvicore.api.ImageDevice;
+import jp.sourceforge.dvibrowser.dvicore.gui.swing.ViewSpec;
+
+
+public class IntRGBImage
+{
+  private final int [] buf;
+  private final int width;
+  private final int height;
+
+  public IntRGBImage(int width, int height) {
+    this.buf = new int [width * height];
+    this.width = width;
+    this.height = height;
+  }
+
+  public IntRGBImage(int [] buf, int width, int height) {
+    this.buf = buf;
+    this.width = width;
+    this.height = height;
+  }
+
+  public int [] getBuffer() { return buf; }
+
+  public int width() { return width; }
+  public int height() { return height; }
+
+  public void fill(int c) {
+    for (int i=0; i<buf.length; i++)
+//      buf[i] = 0xff000000 | c;
+      buf[i] = c;
+  }
+
+  public ImageDevice getImageDevice(DviResolution res, GammaCorrector gc) {
+    return new ImageDeviceImpl(res, gc);
+  }
+  public ImageDevice getImageDevice(DviResolution res) {
+    return getImageDevice(res, null);
+  }
+
+  private class ImageDeviceImpl
+  extends AbstractDevice
+  implements ImageDevice
+  {
+    private final GammaCorrector originalGammaCorrector;
+    private GammaCorrector gammaCorrector;
+    private int maxval;
+    protected ImageDeviceImpl(DviResolution res, GammaCorrector gammaCorrector) {
+      super(res);
+      this.originalGammaCorrector = (gammaCorrector == null) ? ViewSpec.getDefaultGammaCorrector() : gammaCorrector;
+      if (originalGammaCorrector == null)
+        throw new IllegalStateException("Unable to determine the gamma corrector.");
+    }
+
+    public DviRect getBounds() {
+      return new DviRect(-point.x, -point.y, width, height);
+    }
+
+    public void begin(int maxval) {
+      this.maxval = maxval;
+      gammaCorrector = GammaCorrectorCache.wrap(originalGammaCorrector);
+    }
+
+    public void end() {
+    }
+
+    private int ptr = 0;
+//    private int gw;
+//    private int gh;
+    public boolean beginImage(int w, int h)
+    throws DviException
+    {
+//      gw = w;
+//      gh = h;
+      ptr = point.x + point.y * width;
+      return true;
+    }
+    public void endImage() {
+    }
+
+    public void putLine(int [] l_buf, int off, int len)
+    throws DviException
+    {
+      final int color = getColor().toIntRGB();
+      for (int i=0; i<len; i++) {
+        final int alpha10 = gammaCorrector.correctGamma(l_buf[off + i], maxval);
+        if (alpha10 != 0)
+          buf[ptr + i] = blend(buf[ptr + i], color, alpha10);
+      }
+      ptr += width;
+    }
+  }
+
+  private static int blendARGB(int c1, int c2, final int alpha10) {
+    int r, g, b;
+
+    b = c1 & 0xff;
+    b += (alpha10 * ((c2 & 0xff) - b)) >>> 10;
+    b &= 0xff;
+    c1 >>>= 8;
+    c2 >>>= 8;
+
+    g = c1 & 0xff;
+    g += (alpha10 * ((c2 & 0xff) - g)) >>> 10;
+    g &= 0xff;
+    c1 >>>= 8;
+    c2 >>>= 8;
+
+    r = c1 & 0xff;
+    r += (alpha10 * ((c2 & 0xff) - r)) >>> 10;
+    r &= 0xff;
+
+    return 0xff000000 | (r << 16) | (g << 8) | b;
+  }
+  
+  private static int blend(int c1, int c2, final int alpha10) {
+           int r, g, b;
+
+           b = c1 & 0xff;
+           b += (alpha10 * ((c2 & 0xff) - b)) >>> 10;
+           b &= 0xff;
+           c1 >>>= 8;
+           c2 >>>= 8;
+
+           g = c1 & 0xff;
+           g += (alpha10 * ((c2 & 0xff) - g)) >>> 10;
+           g &= 0xff;
+           c1 >>>= 8;
+           c2 >>>= 8;
+
+           r = c1 & 0xff;
+           r += (alpha10 * ((c2 & 0xff) - r)) >>> 10;
+           r &= 0xff;
+
+           return (r << 16) | (g << 8) | b;
+         }
+
+}
+//BenchMark: dvidump: 535 samples in 19.174 sec. 27.902 samples/sec.  35.839 msec./sample.
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/RunLengthSampler.java b/src/jp/sourceforge/dvibrowser/dvicore/render/RunLengthSampler.java
new file mode 100644 (file)
index 0000000..0f22b4a
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviColor;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviPoint;
+import jp.sourceforge.dvibrowser.dvicore.DviRect;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
+import jp.sourceforge.dvibrowser.dvicore.api.ImageDevice;
+
+public class RunLengthSampler
+extends AbstractDevice
+implements BinaryDevice
+{
+  private final ImageDevice out;
+  private final int sf;
+
+  public RunLengthSampler(ImageDevice out)
+  throws DviException
+  {
+    super(out.getResolution());
+    this.out = out;
+    sf = out.getResolution().shrinkFactor();
+  }
+
+  public DviResolution getResolution()
+  throws DviException
+  {
+    return out.getResolution();
+  }
+
+  private int level = 0;
+
+  public void begin()
+  throws DviException
+  {
+    if (level == 0) {
+      point = out.getReferencePoint().magnify(sf);
+      // TODO: fix by a floored mod.
+
+      super.setColor(out.getColor());
+      out.begin(sf*sf);
+      out.save();
+      out.setReferencePoint(DviPoint.ORIGIN);
+    } else {
+      out.save();
+    }
+    level++;
+  }
+
+  public void end()
+  throws DviException
+  {
+    level--;
+    out.restore();
+    if (level == 0) {
+      out.end();
+    }
+  }
+
+/*
+  public void translate(int dx, int dy) throws DviException {
+    super.translate(dx, dy);
+    // TODO: propagate the change to out
+    // if this method is called from outside begin() .. end() block.
+  }
+
+  public void save() throws DviException {
+    super.save();
+  }
+
+  public void restore() throws DviException {
+    super.restore();
+    // TODO: propagate the change to out
+    // if this method is called from outside begin() .. end() block.
+  }
+  */
+
+  public void setColor(DviColor color)
+  throws DviException
+  {
+    super.setColor(color);
+    out.setColor(color);
+  }
+
+  private boolean needPaint = false;
+  private DviRect cpy;
+  private int l_pad_sf;
+
+  private int [] s_buf = null;
+
+  private int cph;
+  private int s_sx;
+  private int s_sy;
+
+  private int yy;
+
+  private int [] l_buf = null;
+  private int [] empty_buf = null;
+
+  public boolean beginRaster(int gw_sf, int gh_sf)
+  throws DviException
+  {
+    out.save();
+    needPaint = false;
+
+    DviRect r_in_sf = new DviRect(point, gw_sf, gh_sf);
+    DviRect r_in = r_in_sf.shrink(sf);
+    l_pad_sf = r_in_sf.x() - r_in.x() * sf;
+    int t_pad_sf = r_in_sf.y() - r_in.y() * sf;
+
+    cpy = r_in;
+    DviRect bounds = out.getBounds();
+    if (bounds != null) {
+      cpy = cpy.intersect(bounds);
+    }
+
+    if (cpy.isEmpty()) {
+      return false;
+    }
+
+    s_buf = new int [cpy.width()];
+    yy = t_pad_sf;
+
+    s_sx = cpy.x() - r_in.x();
+    s_sy = cpy.y() - r_in.y();
+    l_buf = new int [r_in.width()];
+    empty_buf = new int [r_in.width()];
+    cph = cpy.height();
+
+    out.setReferencePoint(cpy.topLeft());
+    needPaint = out.beginImage(cpy.width(), cpy.height());
+    return needPaint;
+  }
+
+  public void endRaster()
+  throws DviException
+  {
+    out.restore();
+    flushLine();
+    out.endImage();
+    s_buf = null;
+    l_buf = null;
+    empty_buf = null;
+  }
+
+  private void flushLine()
+  throws DviException
+  {
+    if (needPaint == false) return;
+    if (cpy.width() == 0) return; // In this case s_buf == null.
+
+    if (s_sy > 0) {
+      s_sy--;
+    } else {
+      if (cph > 0) {
+        out.putLine(s_buf, 0, s_buf.length);
+        cph--;
+      }
+    }
+  }
+
+  private int xx;
+  private int x_pos;
+  public void beginLine()
+  throws DviException
+  {
+    System.arraycopy(empty_buf, 0, l_buf, 0, l_buf.length);
+
+    x_pos = 0;
+    xx = l_pad_sf;
+  }
+
+  public void endLine(int count)
+  throws DviException
+  {
+    count++;
+    final int left = sf - yy;
+    if (left <= count) {
+      for (int j=0; j<s_buf.length; j++)
+        s_buf[j] += l_buf[j + s_sx] * left;
+      flushLine();
+      count -= left;
+      if (count >= sf) {
+        for (int j=0; j<s_buf.length; j++)
+          s_buf[j] = l_buf[j + s_sx] * sf;
+        while (count >= sf) {
+          flushLine();
+          count -= sf;
+        }
+      }
+      System.arraycopy(empty_buf, 0, s_buf, 0, s_buf.length);
+      yy = count;
+    } else {
+      yy += count;
+    }
+
+    if (count > 0) {
+      for (int j=0; j<s_buf.length; j++)
+        s_buf[j] += l_buf[j + s_sx] * count;
+    }
+  }
+
+  public void putBits(int count, boolean paintFlag)
+  throws DviException
+  {
+    if (paintFlag) {
+      final int left = sf - xx;
+      if (left <= count) {
+        l_buf[x_pos++] += left;
+        count -= left;
+        while (count >= sf) {
+          l_buf[x_pos++] += sf;
+          count -= sf;
+        }
+        xx = count;
+      } else {
+        xx += count;
+      }
+      if (count > 0) {
+        l_buf[x_pos] += count;
+      }
+    } else {
+      xx += count;
+      x_pos += xx / sf;
+      xx %= sf;
+      /*
+      while (xx >= sf) {
+        x_pos++;
+        xx -= sf;
+      }
+      */
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/StopHandler.java b/src/jp/sourceforge/dvibrowser/dvicore/render/StopHandler.java
new file mode 100644 (file)
index 0000000..2165d19
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorHandler;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+
+public class StopHandler
+implements DviExecutorHandler
+{
+  private DviExecutorContext ctx;
+  public void begin(DviExecutorContext ctx) throws DviException {
+    this.ctx = ctx;
+  }
+  public void end() throws DviException { ctx = null; }
+
+  public void doSet(int code) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doSetRule(int w, int h) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doPut(int code) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doPutRule(int w, int h) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doNop() throws DviException {
+    ctx.setTerminate(true);
+  }
+
+  public void doSelectFont(int fn) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doDefineFont(int fn, DviFontSpec fs) throws DviException {
+    ctx.setTerminate(true);
+  }
+
+  public void doPush() throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doPop() throws DviException {
+    ctx.setTerminate(true);
+  }
+
+  public void doPre(DviPreamble preamble) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doBop(DviBop bop) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doEop() throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doPost(DviPostamble postamble) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doPostPost(DviPostPost postPost) throws DviException {
+    ctx.setTerminate(true);
+  }
+
+  public void doRight(int by) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doW(int by) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doW0() throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doX(int by) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doX0() throws DviException {
+    ctx.setTerminate(true);
+  }
+
+  public void doDown(int by) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doY(int by) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doY0() throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doZ(int by) throws DviException {
+    ctx.setTerminate(true);
+  }
+  public void doZ0() throws DviException {
+    ctx.setTerminate(true);
+  }
+
+  public void doSpecial(byte [] xxx) throws DviException {
+    ctx.setTerminate(true);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/render/VirtualFontGeometer.java b/src/jp/sourceforge/dvibrowser/dvicore/render/VirtualFontGeometer.java
new file mode 100644 (file)
index 0000000..91f1421
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.render;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+
+public class VirtualFontGeometer 
+extends BasicGeometer
+{
+  private final DviFontSpec fs;
+  private final double factor;
+  public VirtualFontGeometer(DviContextSupport dcs, DviFontSpec fs) {
+    super(dcs);
+    this.fs = fs;
+    factor = (double) fs.spaceSize() / (double)(1 << 20);
+  }
+  
+  public DviFontSpec getFontSpec() {
+         return fs;
+  }
+
+  private int scale(int a) {
+    return (int)(a * factor);
+  }
+
+  public void doSetRule(int width, int height) throws DviException {
+    super.doSetRule(
+      scale(width), scale(height)
+    );
+  }
+
+  public void doPutRule(int width, int height) throws DviException {
+    super.doPutRule(
+      scale(width), scale(height)
+    );
+  }
+
+  public void doRight(int by) throws DviException {
+    super.doRight(scale(by));
+  }
+  public void doW(int by) throws DviException {
+    super.doW(scale(by));
+  }
+  public void doX(int by) throws DviException {
+    super.doX(scale(by));
+  }
+
+  public void doDown(int by) throws DviException {
+    super.doDown(scale(by));
+  }
+  public void doY(int by) throws DviException {
+    super.doY(scale(by));
+  }
+  public void doZ(int by) throws DviException {
+    super.doZ(scale(by));
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/special/AbstractDviSpecialExecutor.java b/src/jp/sourceforge/dvibrowser/dvicore/special/AbstractDviSpecialExecutor.java
new file mode 100644 (file)
index 0000000..c80ada3
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.special;
+
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.render.BasicExecutor;
+
+public abstract class AbstractDviSpecialExecutor
+extends BasicExecutor
+{
+  public AbstractDviSpecialExecutor(DviContextSupport dcs) {
+    super(dcs);
+  }
+
+  public boolean wantSet()        { return false; }
+  public boolean wantPut()        { return false; }
+  public boolean wantSelectFont() { return false; }
+  public boolean wantSpecial()    { return true;  }
+  public boolean wantSetRule()    { return false; }
+  public boolean wantPutRule()    { return false; }
+  public boolean wantDefineFont() { return false; }
+  public boolean wantRight()      { return false; }
+  public boolean wantW()          { return false; }
+  public boolean wantW0()         { return false; }
+  public boolean wantX()          { return false; }
+  public boolean wantX0()         { return false; }
+  public boolean wantDown()       { return false; }
+  public boolean wantY()          { return false; }
+  public boolean wantY0()         { return false; }
+  public boolean wantZ()          { return false; }
+  public boolean wantZ0()         { return false; }
+  public boolean wantPush()       { return false; }
+  public boolean wantPop()        { return false; }
+  public boolean wantNop()        { return false; }
+  public boolean wantBop()        { return false; }
+  public boolean wantEop()        { return false; }
+  public boolean wantPre()        { return false; }
+  public boolean wantPost()       { return false; }
+  public boolean wantPostPost()   { return false; }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/special/Anchor.java b/src/jp/sourceforge/dvibrowser/dvicore/special/Anchor.java
new file mode 100644 (file)
index 0000000..48432d4
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.special;
+
+import jp.sourceforge.dvibrowser.dvicore.DviByteRange;
+
+public class Anchor
+extends DviByteRange
+{
+  public Anchor(long begin, long end) {
+    super(begin, end);
+  }
+
+  public static class StringValued
+  extends Anchor
+  {
+    private String str;
+
+    public StringValued(long begin, long end, String str) {
+      super(begin, end);
+      this.str = str;
+    }
+    public String string() { return str; }
+
+    public String toString() {
+      return getClass().getName()
+        + "[range=" + super.toString()
+        + ",str=" + str
+        + "]";
+    }
+  }
+
+  public static class Href
+  extends StringValued
+  {
+    public Href(long begin, long end, String str) {
+      super(begin, end, str);
+    }
+    public String getLocation() { return string(); }
+  }
+
+  public static class Name
+  extends StringValued
+  {
+    public Name(long begin, long end, String str) {
+      super(begin, end, str);
+    }
+    public String name() { return string(); }
+  }
+  
+  public static class Source
+  extends StringValued
+  {
+    private final int lineNumber;
+    public Source(long begin, long end, String str, int lineNumber) {
+      super(begin, end, str);
+      this.lineNumber = lineNumber;
+    }
+    public String getFilename() { return string(); }
+    public int getLineNumber()
+    {
+      return lineNumber;
+    }
+    public String toString() {
+      return getClass().getName()
+        + "[range=" + super.toString()
+        + ",filename=" + string()
+        + ",lineNumber=" + lineNumber
+        + "]";
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/special/AnchorSet.java b/src/jp/sourceforge/dvibrowser/dvicore/special/AnchorSet.java
new file mode 100644 (file)
index 0000000..2cc7385
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.special;
+
+public class AnchorSet
+extends ByteRangeSet
+{
+       private static final long serialVersionUID = -1414405820823767279L;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/special/ByteRangeSet.java b/src/jp/sourceforge/dvibrowser/dvicore/special/ByteRangeSet.java
new file mode 100644 (file)
index 0000000..6ff5b44
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.special;
+
+import java.util.Comparator;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import jp.sourceforge.dvibrowser.dvicore.DviByteRange;
+
+
+public class ByteRangeSet
+extends TreeSet<DviByteRange>
+{
+       private static final long serialVersionUID = -5733122326062424799L;
+
+  public ByteRangeSet()
+  {
+    super(
+      new Comparator<DviByteRange>() {
+        public int compare(DviByteRange a, DviByteRange b) {
+          long _a = a.begin();
+          long _b = b.begin();
+          return (_a < _b) ? -1
+             : (_a == _b) ? 0
+             : 1;
+        }
+      }
+    );
+  }
+
+  public SortedSet<DviByteRange> intersect(DviByteRange range)
+  {
+    return subSet(
+      new DviByteRange(range.begin(), range.begin()),
+      new DviByteRange(range.end()+1, range.end()+1)
+    );
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/special/EPS2ImagePreparator.java b/src/jp/sourceforge/dvibrowser/dvicore/special/EPS2ImagePreparator.java
new file mode 100644 (file)
index 0000000..2696c19
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.special;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.imageio.ImageIO;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.api.HasURL;
+import jp.sourceforge.dvibrowser.dvicore.ctx.DviToolkit;
+import jp.sourceforge.dvibrowser.dvicore.gs.GhostscriptCommandBuilder;
+import jp.sourceforge.dvibrowser.dvicore.gui.swing.ViewSpec;
+import jp.sourceforge.dvibrowser.dvicore.image.split.DviImage;
+import jp.sourceforge.dvibrowser.dvicore.util.CommandShell;
+import jp.sourceforge.dvibrowser.dvicore.util.CommandShellHandler;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+import jp.sourceforge.dvibrowser.dvicore.util.LineBuffer;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.Computation;
+
+
+public class EPS2ImagePreparator
+extends DviObject
+implements Computation<String, DviImage> {
+  private static final Logger LOGGER = Logger
+      .getLogger(EPS2ImagePreparator.class.getName());
+
+  private final DviPage page;
+  private final ViewSpec viewSpec;
+  private final String postScriptData;
+  private final String hash;
+
+  public EPS2ImagePreparator(DviContextSupport dcs, DviPage page, ViewSpec viewSpec)
+  throws DviException
+  {
+    super(dcs);
+    this.page = page;
+    this.viewSpec = viewSpec;
+    DviToolkit utils = getDviContext().getDviToolkit();
+    this.postScriptData = utils.getEmbeddedPostScript(page, viewSpec);
+    this.hash = DviUtils.md5Hex(postScriptData);
+  }
+
+  public DviImage call() throws Exception
+  {
+    if (postScriptData == null) return null;
+    
+    File dir = getDviContext().getTemporaryDirectory();
+    String key = getCacheKey();
+    String keyHash = DviUtils.md5Hex(key);
+    String basename = String.format("dvibrowser--%s--%08d", keyHash, page.getPageNumber());
+    String imgExt = getViewSpec().getEpsImageFileExtension();
+    File imgFile = new File(dir, basename + imgExt);
+    if (!imgFile.exists()) {
+      LOGGER.fine("Now creating postscript image for page " + page);
+      LOGGER.fine("Using key=" + key);
+      LOGGER.fine("Using keyHash=" + keyHash);
+      LOGGER.finer("PostScriptData= " + postScriptData);
+      File psFile = new File(dir, basename + ".ps");
+      File tmpFile = File.createTempFile(basename, imgExt, dir);
+      try {
+        psFile.deleteOnExit();
+        DviUtils.writeStringToFile(psFile, postScriptData);
+        GhostscriptCommandBuilder gs = new GhostscriptCommandBuilder(this);
+        gs.setInputFile(psFile);
+        gs.setOutputFile(tmpFile);
+        gs.setResolution(viewSpec.getEpsResolutionDpi());
+        gs.setDevice(getViewSpec().getEpsImageDeviceName());
+        CommandShell shell = gs.createCommandShell();
+        // TODO: outsource the max line numbers to safe.
+        final LineBuffer<String> stderr = new LineBuffer<String>(100);
+        final LineBuffer<String> stdout = new LineBuffer<String>(100);
+        shell.setHandler(new CommandShellHandler() {
+          public void handleStderr(InputStream in) throws IOException {
+            DviUtils.accumulateToLineBuffer(in, stderr);
+          }
+          public void handleStdin(OutputStream out) throws IOException {
+            out.close();
+          }
+          public void handleStdout(InputStream in) throws IOException {
+            DviUtils.accumulateToLineBuffer(in, stdout);
+          }
+        });
+        int ret = shell.execute();
+        if (ret == CommandShell.RETCODE_SUCCESS) {
+          if (!tmpFile.renameTo(imgFile)) {
+            throw new DviException("Failed to write image file: " + imgFile);
+          }
+          imgFile.deleteOnExit();
+          LOGGER.fine("Saved image to " + imgFile);
+        } else {
+          LOGGER.warning("Ghostscript failed with retcode " + ret + ".");
+          LOGGER.warning("Ghosctscript stdout: " + stdout);
+          LOGGER.warning("Ghosctscript stderr: " + stderr);
+        }
+      } finally {
+        LOGGER.fine("Finished creating postscript image for page " + page);
+        if (DELETE_PSFILE_IMMEDIATELY) {
+          psFile.delete();
+        }
+        tmpFile.delete();
+      }
+    }
+    DviImage dviImage;
+    try {
+      BufferedImage image = ImageIO.read(imgFile);
+      dviImage = null;
+      if (image != null) {
+        dviImage = new DviImage(image, viewSpec.getEpsResolutionDpi());
+      }
+    } catch (OutOfMemoryError e) {
+      LOGGER.severe("Unable to render image: imgFile=" + imgFile + " filesize=" + imgFile.length());
+      DviUtils.logStackTrace(LOGGER, Level.SEVERE, e);
+      throw e;
+    }
+    
+    return dviImage;
+  }
+  
+  private static final boolean DELETE_PSFILE_IMMEDIATELY = false;
+
+  public String getCacheKey()
+  {
+    // TODO: use viewSpec.
+    String pageKey = null;
+    try {
+      DviDocument doc = page.getDocument();
+      if (doc instanceof HasURL) {
+        URL url = ((HasURL) doc).getURL();
+        pageKey = url.toExternalForm() + "#page" + page.getPageNumber();
+      }
+    } catch (DviException e) {
+      LOGGER.warning(e.toString());
+      pageKey = page.getCacheKey();
+    }
+    return getClass().getName() + "--" + hash + "-" + pageKey;
+  }
+
+  public ViewSpec getViewSpec()
+  {
+    return viewSpec;
+  }
+
+  public DviPage getPage()
+  {
+    return page;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/special/EPS2SplitImagePreparator.java b/src/jp/sourceforge/dvibrowser/dvicore/special/EPS2SplitImagePreparator.java
new file mode 100644 (file)
index 0000000..c05a92f
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.special;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.DviSize;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.api.HasURL;
+import jp.sourceforge.dvibrowser.dvicore.ctx.DviToolkit;
+import jp.sourceforge.dvibrowser.dvicore.gs.GhostscriptUtils;
+import jp.sourceforge.dvibrowser.dvicore.gui.swing.ViewSpec;
+import jp.sourceforge.dvibrowser.dvicore.image.split.ImageFileConfig;
+import jp.sourceforge.dvibrowser.dvicore.image.split.SplitImage;
+import jp.sourceforge.dvibrowser.dvicore.image.split.ZipSplitImageReader;
+import jp.sourceforge.dvibrowser.dvicore.image.split.ZipSplitImageWriter;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+import jp.sourceforge.dvibrowser.dvicore.util.ZipBuilder;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.Computation;
+
+
+public class EPS2SplitImagePreparator
+extends DviObject
+implements Computation<String, SplitImage> {
+  private static final Logger LOGGER = Logger
+      .getLogger(EPS2SplitImagePreparator.class.getName());
+
+  private final DviPage page;
+  private final ViewSpec viewSpec;
+  private final String postScriptData;
+  private final String hash;
+
+  public EPS2SplitImagePreparator(DviContextSupport dcs, DviPage page, ViewSpec viewSpec)
+  throws DviException
+  {
+    super(dcs);
+    this.page = page;
+    this.viewSpec = viewSpec;
+    DviToolkit utils = getDviContext().getDviToolkit();
+    this.postScriptData = utils.getEmbeddedPostScript(page, viewSpec);
+    this.hash = DviUtils.md5Hex(postScriptData);
+  }
+
+  public SplitImage call() throws Exception
+  {
+    if (postScriptData == null) return null;
+    
+    File dir = getDviContext().getTemporaryDirectory();
+    String key = getCacheKey();
+    String keyHash = DviUtils.md5Hex(key);
+    String basename = String.format("gs--%s--%08d", keyHash, page.getPageNumber());
+    String imgExt = ".zip";
+    // TODO: outsource the resolution configuration.
+    DviResolution res = new DviResolution(400, 4);
+    DviSize unit = new DviSize(256, 256);
+    File imgFile = new File(dir, basename + imgExt);
+    imgFile.deleteOnExit();
+    if (!imgFile.exists()) {
+      LOGGER.fine("Now creating postscript image for page " + page);
+      LOGGER.fine("Using key=" + key);
+      LOGGER.fine("Using keyHash=" + keyHash);
+      LOGGER.finer("PostScriptData= " + postScriptData);
+      try {
+        File tmpFile = File.createTempFile("gs-temp", null);
+        tmpFile.deleteOnExit();
+        try {
+          final FileOutputStream fos = new FileOutputStream(tmpFile);
+          final ZipBuilder zb = new ZipBuilder(fos);
+          // TODO: outsource the encoding configuration.
+          final InputStream is = new ByteArrayInputStream(postScriptData.getBytes("UTF-8")); 
+          try {
+            final ZipSplitImageWriter imageWriter = new ZipSplitImageWriter
+              (basename, ImageFileConfig.PNG, res, zb);
+            // Remark that Ghostscript counts pages from 1 not 0.
+            GhostscriptUtils.renderAndSplit(getDviContext(), is, unit, imageWriter, res, "pnmraw", 1, 0, null);
+          } finally {
+            DviUtils.silentClose(fos);
+            DviUtils.silentClose(is);
+          }
+          // TODO: lock before renaming to avoid race condition against other thread.
+          if (!tmpFile.renameTo(imgFile)) {
+            throw new DviException("Failed to create cache file: " + imgFile);
+          }
+        } finally {
+          tmpFile.delete();
+        }
+      } catch (FileNotFoundException e) {
+        DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+        throw new DviException(e);
+      } catch (IOException e) {
+        DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
+        throw new DviException(e);
+      }
+    }
+    ZipSplitImageReader imageReader = new ZipSplitImageReader(imgFile);
+    return imageReader.getSplitImage();
+  }
+  
+  public String getCacheKey()
+  {
+    // TODO: use viewSpec.
+    String pageKey = null;
+    try {
+      DviDocument doc = page.getDocument();
+      if (doc instanceof HasURL) {
+        URL url = ((HasURL) doc).getURL();
+        pageKey = url.toExternalForm() + "#page" + page.getPageNumber();
+      }
+    } catch (DviException e) {
+      LOGGER.warning(e.toString());
+      pageKey = page.getCacheKey();
+    }
+    return getClass().getName() + "--" + hash + "-" + pageKey;
+  }
+
+  public ViewSpec getViewSpec()
+  {
+    return viewSpec;
+  }
+
+  public DviPage getPage()
+  {
+    return page;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/special/EmbeddedPostScript.java b/src/jp/sourceforge/dvibrowser/dvicore/special/EmbeddedPostScript.java
new file mode 100644 (file)
index 0000000..053d94d
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.special;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Vector;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+public class EmbeddedPostScript
+{
+//  private static final Logger LOGGER = Logger.getLogger(EmbeddedPostScript.class.getName());
+  
+  private final Vector<Prologue> prologues = new Vector<Prologue>();
+  private final Vector<Global> globals = new Vector<Global>();
+  private final HashMap<Integer, Vector<Local>> page2locals
+    = new HashMap<Integer, Vector<Local>>();
+
+  private Vector<Local> locals = null;
+
+  public void beginPage(int pageNum)
+  {
+    if (page2locals.containsKey(pageNum)) {
+      locals = page2locals.get(pageNum);
+    } else {
+      locals = new Vector<Local>();
+      page2locals.put(pageNum, locals);
+    }
+  }
+
+  public void endPage()
+  {
+    locals = null;
+  }
+
+  public void add(Prologue a)
+  {
+    prologues.add(a);
+  }
+
+  public void add(Global a)
+  {
+    globals.add(a);
+  }
+
+  public void add(Local a)
+  {
+    locals.add(a);
+  }
+
+  private static void writePostScript(Vector<? extends Element> ce,
+      PrintWriter pw, Config cfg) throws DviException
+  {
+    for (Element e : ce) {
+      e.writePostScript(pw, cfg);
+    }
+  }
+
+  public String toPostScript(int pageNum, int dpi) 
+  throws DviException
+  {
+    if (!page2locals.containsKey(pageNum))
+      return null;
+
+    Vector<Local> ls = page2locals.get(pageNum);
+
+    if (ls == null || ls.size() == 0)
+      return null;
+
+    Config cfg = new Config(dpi);
+
+    try {
+      StringWriter sw = new StringWriter();
+      PrintWriter pw = new PrintWriter(sw);
+
+      pw.println("%!PS-Adobe-2.0");
+      pw.println("%%Pages: 1");
+      pw.println("%%BoundingBox: 0 0 595 842"); // Change bounding box depending on paper sizes.
+      pw.println("%%EndComments");
+      pw.println("%!");
+
+      writePostScript(prologues, pw, cfg);
+
+      // TODO: Use the paper size to set the bounding boxes
+      pw.println(
+          "TeXDict begin"
+        + " 39158280 55380996 "
+        + " 1000 "
+        + dpi + " " + dpi
+        + " (a.dvi) "
+        + " @start end "
+      );
+      pw.println("TeXDict begin 1 0 bop 0 0 a");
+
+      writePostScript(globals, pw, cfg);
+
+      writePostScript(ls, pw, cfg);
+
+      pw.println("end");
+      pw.println("showpage");
+      pw.close();
+      sw.flush();
+      return sw.toString();
+    } catch (Exception ex) {
+      throw new DviException(ex);
+    }
+  }
+
+  private static class Config
+  {
+    public final int dpi;
+    private Config(int dpi) {
+      this.dpi = dpi;
+    }
+  }
+
+  private static interface Element
+  {
+    public abstract void writePostScript(PrintWriter pw, Config cfg) throws DviException;
+  }
+
+  public static interface Local extends Element {}
+  public static interface Global extends Element {}
+  public static interface Prologue extends Element {}
+
+  public static class ProloguePostScript
+  {
+    public final String postScript;
+    public ProloguePostScript(String postScript) {
+      this.postScript = postScript;
+    }
+
+    public void writePostScript(PrintWriter pw, Config cfg)
+    {
+      pw.println(postScript);
+    }
+  }
+  
+  // TODO: support encoding.
+  private static String escapeFilenameForPS(String s)
+  {
+    return s.replaceAll("\\\\", "\\\\\\\\");
+  }
+
+  public static class PrologueFile
+  extends DviObject
+  implements Prologue
+  {
+    public final String fileName;
+    public PrologueFile(DviContextSupport dcs, String fileName) {
+      super(dcs);
+      this.fileName = fileName;
+    }
+
+    public void writePostScript(PrintWriter pw, Config cfg) throws DviException
+    {
+      File file = DviUtils.toLocalFile(getDviContext().getDviResource(fileName));
+      if (file == null || !file.exists())
+        throw new DviException("Cannot find postscript prologue: " + fileName);
+
+      String escapedFilename = escapeFilenameForPS(file.getAbsolutePath());
+      pw.println(
+          " (" + escapedFilename + ") run"
+      );
+    }
+  }
+
+  public static class HeaderSpecial
+  implements Global
+  {
+    public final String fileName;
+    public HeaderSpecial(String fileName) {
+      this.fileName = fileName;
+    }
+
+    public void writePostScript(PrintWriter pw, Config cfg)
+    {
+      String escapedFilename = escapeFilenameForPS(fileName);
+      pw.println(
+        " (" + escapedFilename + ") run"
+      );
+    }
+  }
+
+  public static class BangSpecial
+  implements Global
+  {
+    public final String postScript;
+    public BangSpecial(String postScript) {
+      this.postScript = postScript;
+    }
+
+    public void writePostScript(PrintWriter pw, Config cfg)
+    {
+      pw.println(
+        " @defspecial " + postScript + " @fedspecial"
+      );
+    }
+  }
+
+  private static class WithReferencePoint
+  implements Local
+  {
+    public final int h;
+    public final int v;
+    public final DviUnit dviUnit;
+    private WithReferencePoint(int h, int v, DviUnit dviUnit)
+    {
+      this.h = h;
+      this.v = v;
+      this.dviUnit = dviUnit;
+    }
+
+    public void writePostScript(PrintWriter pw, Config cfg)
+    {
+      double psH = dviUnit.mapToPixelDouble(h, cfg.dpi);
+      double psV = dviUnit.mapToPixelDouble(v, cfg.dpi);
+      pw.println(
+        " " + psH + " " + psV + " moveto "
+      );
+    }
+  }
+
+  public static class PSFileSpecial
+  extends WithReferencePoint
+  {
+    public final String fileName;
+    public final int llx;
+    public final int lly;
+    public final int urx;
+    public final int ury;
+    public final int rwi;
+    public final int rhi;
+    public final int angle;
+    public PSFileSpecial(
+      int h, int v, DviUnit dviUnit,
+      String fileName,
+      int llx, int lly, int urx, int ury,
+      int rwi, int rhi, int angle
+    ) {
+      super(h, v, dviUnit);
+      this.fileName = fileName;
+      this.llx = llx;
+      this.lly = lly;
+      this.urx = urx;
+      this.ury = ury;
+      this.rwi = rwi;
+      this.rhi = rhi;
+      this.angle = angle;
+    }
+
+    public void writePostScript(PrintWriter pw, Config cfg)
+    {
+      super.writePostScript(pw, cfg);
+      String escapedFilename = escapeFilenameForPS(fileName);
+//      if (new File(realFile).exists()) {
+        pw.println(
+            " @beginspecial"
+          + " " + llx + " @llx"
+          + " " + lly + " @lly"
+          + " " + urx + " @urx"
+          + " " + ury + " @ury"
+          + ((rwi != 0) ? (" " + rwi + " @rwi") : "")
+          + ((rhi != 0) ? (" " + rhi + " @rhi") : "")
+          + ((angle != 0) ? (" " + angle + " @angle") : "")
+          + " @setspecial"
+          + " (" + escapedFilename + ") run"
+          + " @endspecial"
+        );
+//      } else {
+//      }
+    }
+  }
+
+  public static class PSSpecial
+  extends WithReferencePoint
+  {
+    public final String postScript;
+    public PSSpecial(int h, int v, DviUnit dviUnit, String postScript)
+    {
+      super(h, v, dviUnit);
+      this.postScript = postScript;
+    }
+
+    public void writePostScript(PrintWriter pw, Config cfg)
+    {
+      super.writePostScript(pw, cfg);
+      pw.println(
+        " " + postScript
+      );
+    }
+  }
+
+  public static class QuoteSpecial
+  extends PSSpecial
+  {
+    public QuoteSpecial(int h, int v, DviUnit dviUnit, String postScript)
+    {
+      super(h, v, dviUnit, postScript);
+    }
+
+    public void writePostScript(PrintWriter pw, Config cfg)
+    {
+      super.writePostScript(pw, cfg);
+      pw.println(
+          " @beginspecial"
+        + " @setspecial"
+        + " " + postScript
+        + " @endspecial"
+      );
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/special/EmbeddedPostScriptPreparator.java b/src/jp/sourceforge/dvibrowser/dvicore/special/EmbeddedPostScriptPreparator.java
new file mode 100644 (file)
index 0000000..31f208d
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.special;
+import java.io.File;
+import java.net.URL;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.Geometer;
+import jp.sourceforge.dvibrowser.dvicore.api.HasURL;
+import jp.sourceforge.dvibrowser.dvicore.gui.swing.ViewSpec;
+import jp.sourceforge.dvibrowser.dvicore.render.BasicGeometer;
+import jp.sourceforge.dvibrowser.dvicore.render.EmptyBinaryDevice;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.Computation;
+import jp.sourceforge.dvibrowser.dvicore.util.progress.ProgressItem;
+
+
+public class EmbeddedPostScriptPreparator
+extends DviObject
+implements Computation<String, EmbeddedPostScript> {
+  private static final Logger LOGGER = Logger.getLogger(EmbeddedPostScriptPreparator.class
+      .getName());
+  private final DviDocument doc;
+  private final ViewSpec viewSpec;
+  
+  public EmbeddedPostScriptPreparator(DviContextSupport dcs, DviDocument doc, ViewSpec viewSpec)
+  {
+    super(dcs);
+    this.doc = doc;
+    this.viewSpec = viewSpec;
+  }
+
+  public EmbeddedPostScript call() throws Exception
+  {
+    if (doc == null) return null;
+    final ProgressItem progress = getDviContext().getProgressRecorder().open("extracting EPS data");
+    try {
+      PostScriptSpecialParser pse = new PostScriptSpecialParser(this);
+      if (doc instanceof HasURL) {
+        URL url = ((HasURL) doc).getURL();
+        if (DviUtils.isFile(url)) {
+          File file = new File(url.getPath());
+          File dir = file.getParentFile();
+          LOGGER.finer("working dir=" + dir);
+          pse.setWorkDirectory(dir);
+        } else {
+          LOGGER.finer("Using default working dir");
+        }
+      } else {
+        LOGGER.finer("No URL is available for doc " + doc);
+      }
+      pse.getEmbeddedPostScript().add(
+          new EmbeddedPostScript.PrologueFile(this, "texc.pro"));
+      pse.getEmbeddedPostScript().add(
+          new EmbeddedPostScript.PrologueFile(this, "special.pro"));
+      // This is a dummy output.
+      // Should we change this to use viewSpec instead?
+      pse.setOutput(new EmptyBinaryDevice(new DviResolution(600, 4)));
+
+      Geometer gm = new BasicGeometer(this);
+      gm.setPainter(pse);
+      getDviContext().execute(doc, gm);
+      EmbeddedPostScript eps = pse.getEmbeddedPostScript();
+      return eps;
+    } finally {
+      progress.close();
+    }
+  }
+
+  public String getCacheKey()
+  {
+    return "EPS--" + doc.getCacheKey();
+  }
+
+  public DviDocument getDocument()
+  {
+    return doc;
+  }
+
+  public ViewSpec getViewSpec()
+  {
+    return viewSpec;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/special/HtmlSpecialParser.java b/src/jp/sourceforge/dvibrowser/dvicore/special/HtmlSpecialParser.java
new file mode 100644 (file)
index 0000000..93f031c
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.special;
+
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+
+
+// TODO: support Logging
+public class HtmlSpecialParser
+extends DviObject
+{
+  private final AnchorSet anchorSet = new AnchorSet();
+  private final PrivateExecuter exe;
+
+  public HtmlSpecialParser(DviContextSupport dcs)
+  {
+    super(dcs);
+    exe = new PrivateExecuter(dcs, anchorSet);
+  }
+
+  private static final Pattern hrefPat
+    = Pattern.compile(
+      "\\s*html:\\s*<a\\s*href\\s*=\\s*\"(.*)\"\\s*>",
+      Pattern.CASE_INSENSITIVE
+    );
+  private static final Pattern namePat
+    = Pattern.compile(
+      "\\s*html:\\s*<a\\s*name\\s*=\\s*\"(.*)\"\\s*>",
+      Pattern.CASE_INSENSITIVE
+    );
+  private static final Pattern endPat
+    = Pattern.compile(
+      "\\s*html:\\s*</a>",
+      Pattern.CASE_INSENSITIVE
+    );
+  
+  public void execute(DviDocument doc)
+  throws DviException
+  {
+    for (int p=0; p<doc.getTotalPages(); p++) {
+      exe.execute(doc.getPage(p));
+    }
+  }
+
+  public AnchorSet getAnchorSet()
+  {
+    return anchorSet;
+  }
+
+  private static class PrivateExecuter
+  extends AbstractDviSpecialExecutor
+  {
+    private final AnchorSet anchorSet;
+    public PrivateExecuter(DviContextSupport dcs, AnchorSet anchorSet) {
+      super(dcs);
+      this.anchorSet = anchorSet;
+    }
+
+    private static class StackItem
+    {
+      private final long start;
+      private final String tag;
+      private final String data;
+      public StackItem(long start, String tag, String data) {
+        this.start = start;
+        this.tag = tag;
+        this.data = data;
+      }
+    }
+
+    private final Stack<StackItem> stack
+      = new Stack<StackItem>();
+
+    public void doSpecial(byte [] _xxx)
+    throws DviException
+    {
+      String xxx = new String(_xxx);
+      Matcher mat;
+      DviExecutorContextImpl ctx = getExecutorContext();
+      if ((mat = hrefPat.matcher(xxx)).matches()) {
+        stack.push(
+          new StackItem(
+            ctx.getCommandRange().begin(),
+            "href",
+            mat.group(1)
+          )
+        );
+      } else if ((mat = namePat.matcher(xxx)).matches()) {
+        stack.push(
+          new StackItem(
+            ctx.getCommandRange().begin(),
+            "name",
+            mat.group(1)
+          )
+        );
+      } else if ((mat = endPat.matcher(xxx)).matches()) {
+        Anchor el = null;
+        if (!stack.empty()) {
+          StackItem it = stack.pop();
+          if ("href".equals(it.tag)) {
+            el = new Anchor.Href(
+              it.start,
+              ctx.getCommandRange().end(),
+              it.data
+            );
+          } else if ("name".equals(it.tag)) {
+            el = new Anchor.Name(
+              it.start,
+              ctx.getCommandRange().end(),
+              it.data
+            );
+          }
+        } else {
+          // TODO: logging
+//          System.out.println("html tag stack underflow.");
+          // This is an error.  We ignore it.
+        }
+        if (el != null) {
+          anchorSet.add(el);
+        }
+      }
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/special/PostScriptSpecialParser.java b/src/jp/sourceforge/dvibrowser/dvicore/special/PostScriptSpecialParser.java
new file mode 100644 (file)
index 0000000..137a7e9
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.special;
+
+import java.io.File;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviRegister;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviBop;
+import jp.sourceforge.dvibrowser.dvicore.render.DefaultDevicePainter;
+
+
+public class PostScriptSpecialParser
+extends DefaultDevicePainter
+{
+  private static final Logger LOGGER = Logger.getLogger(PostScriptSpecialParser.class.getName());
+  
+  private static final Pattern patPS
+    = Pattern.compile(
+      "\\s*ps:\\s*(.*)",
+      Pattern.CASE_INSENSITIVE
+    );
+  private static final Pattern patHeader
+    = Pattern.compile(
+      "\\s*header\\s*=\\s*(.*)",
+      Pattern.CASE_INSENSITIVE
+    );
+  private static final Pattern patPSFile
+    = Pattern.compile(
+      "\\s*PSFile\\s*=\\s*([^\\s]*)\\s+(.*)\\s*",
+      Pattern.CASE_INSENSITIVE
+    );
+  
+  public PostScriptSpecialParser(DviContextSupport dcs)
+  {
+    super(dcs);
+  }
+  
+  private File workDir;
+  public void setWorkDirectory(File dir)
+  {
+    this.workDir = dir;
+  }
+  public File getWorkDirectory()
+  {
+    return workDir;
+  }
+
+  private final EmbeddedPostScript eps = new EmbeddedPostScript();
+
+  public EmbeddedPostScript getEmbeddedPostScript()
+  {
+    return eps;
+  }
+
+  private int pageNum = 0;
+
+  public void beginPage(DviBop bop)
+  {
+    eps.beginPage(pageNum);
+  }
+
+  public void endPage()
+  {
+    eps.endPage();
+    pageNum++;
+  }
+
+  public void beginFont(DviFontSpec fs)
+  {
+  }
+
+  public void endFont()
+  {
+  }
+
+  ////////////
+
+  public void drawChar(int code)
+  {
+    // ignored.
+  }
+
+  public void drawRule(int w, int h)
+  {
+    // ignored.
+  }
+  
+  protected String toAbsoluteFilename(String filename)
+  {
+    File file = new File(workDir, filename);
+    LOGGER.finer("Resolved filename=" + filename + " to " + file.getAbsolutePath());
+    return file.getAbsolutePath();
+  }
+
+  public void drawSpecial(byte [] _xxx)
+  {
+    String xxx = new String(_xxx);
+    Matcher mat;
+
+    LOGGER.finer("handling special: " + xxx);
+    if (xxx.startsWith("\"")) {
+      DviRegister reg = getGeometerContext().getRegister();
+      eps.add(
+        new EmbeddedPostScript.QuoteSpecial(
+          reg.getH(), reg.getV(),
+          getDviUnit(),
+          xxx.substring(1).trim()
+        )
+      );
+    } else if (xxx.startsWith("!")) {
+      eps.add(
+        new EmbeddedPostScript.BangSpecial(
+          xxx.substring(1).trim()
+        )
+      );
+    } else if ((mat = patPS.matcher(xxx)).matches()) {
+      DviRegister reg = getGeometerContext().getRegister();
+      eps.add(
+        new EmbeddedPostScript.PSSpecial(
+          reg.getH(), reg.getV(),
+          getDviUnit(),
+          mat.group(1)
+        )
+      );
+    } else if ((mat = patHeader.matcher(xxx)).matches()) {
+      LOGGER.finer("PSHeader special found: " + xxx);
+      eps.add(
+        new EmbeddedPostScript.HeaderSpecial(
+          trimQuotes(mat.group(1))
+        )
+      );
+    } else if ((mat = patPSFile.matcher(xxx)).matches()) {
+      LOGGER.finer("page=" + pageNum + " PSFile special found: " + xxx);
+      String fileName = toAbsoluteFilename(trimQuotes(mat.group(1)));
+      String options = mat.group(2);
+      int llx = 0;
+      int lly = 0;
+      int urx = 0;
+      int ury = 0;
+      int rwi = 0;
+      int rhi = 0;
+      int angle = 0;
+      String [] os = Pattern.compile("\\s+").split(options);
+      for (int i=0; i<os.length; i++) {
+        Matcher mat2 = Pattern.compile("([a-z]+)=(-?[.0-9]+)").matcher(os[i]);
+        if (mat2.matches()) {
+          try {
+            String var = mat2.group(1);
+            int val = Integer.parseInt(mat2.group(2));
+            if ("llx".equals(var)) llx = val;
+            if ("lly".equals(var)) lly = val;
+            if ("urx".equals(var)) urx = val;
+            if ("ury".equals(var)) ury = val;
+            if ("rwi".equals(var)) rwi = val;
+            if ("rhi".equals(var)) rhi = val;
+            if ("angle".equals(var)) angle = val;
+          } catch (NumberFormatException ex) {
+            // ignored.
+          }
+        }
+      }
+      DviRegister reg = getGeometerContext().getRegister();
+      eps.add(
+        new EmbeddedPostScript.PSFileSpecial(
+          reg.getH(), reg.getV(),
+          getDviUnit(),
+          fileName,
+          llx, lly, urx, ury, rwi, rhi, angle
+        )
+      );
+    }
+    /* Support other types of specials:
+    } else if (xxx.equals("bk")) {
+    } else if (xxx.equals("fp")) {
+    } else if (xxx.startsWith("pn ")) {
+    } else if (xxx.startsWith("ar ")) {
+    } else if (xxx.startsWith("pa ")) {
+    } else if (xxx.startsWith("dt ")) {
+
+    } else if (xxx.startsWith("em:")) {
+    } else if (xxx.startsWith("HP:")) {
+    */
+
+  }
+
+  private static String trimQuotes(String str) {
+    if (str == null) return null;
+    while (str.startsWith("\"")) {
+      str = str.substring(1);
+    }
+    while (str.endsWith("\"")) {
+      str = str.substring(0, str.length()-1);
+    }
+    return str;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/special/SourceSpecialParser.java b/src/jp/sourceforge/dvibrowser/dvicore/special/SourceSpecialParser.java
new file mode 100644 (file)
index 0000000..2e0c0bb
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.special;
+
+import java.util.Stack;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+
+
+public class SourceSpecialParser
+extends DviObject
+{
+  private static final Logger LOGGER = Logger.getLogger(SourceSpecialParser.class.getName());
+  private final AnchorSet anchorSet = new AnchorSet();
+  private final PrivateExecuter exe;
+
+  public SourceSpecialParser(DviContextSupport dcs)
+  {
+    super(dcs);
+    exe = new PrivateExecuter(dcs, anchorSet);
+  }
+
+  private static final Pattern srcPat
+    = Pattern.compile(
+      "\\s*src:\\s*([-0-9]*)\\s\\s*(.*)\\s*",
+      Pattern.CASE_INSENSITIVE
+    );
+  
+  public void execute(DviDocument doc)
+  throws DviException
+  {
+    for (int p=0; p<doc.getTotalPages(); p++) {
+      exe.execute(doc.getPage(p));
+    }
+  }
+
+  public AnchorSet getAnchorSet()
+  {
+    return anchorSet;
+  }
+
+  private static class PrivateExecuter
+  extends AbstractDviSpecialExecutor
+  {
+    private final AnchorSet anchorSet;
+    public PrivateExecuter(DviContextSupport dcs, AnchorSet anchorSet) {
+      super(dcs);
+      this.anchorSet = anchorSet;
+    }
+
+    private static class StackItem
+    {
+      private final long start;
+      private final String tag;
+      private final String data;
+      private final int lineNumber;
+      public StackItem(long start, String tag, String data, int lineNumber) {
+        this.start = start;
+        this.tag = tag;
+        this.data = data;
+        this.lineNumber = lineNumber;
+      }
+    }
+
+    private final Stack<StackItem> stack
+      = new Stack<StackItem>();
+    
+    @Override
+    public void end() throws DviException {
+      flushStack();
+      super.end();
+    }
+    
+    private void flushStack()
+    {
+      if (!stack.empty()) {
+        StackItem it = stack.pop();
+        DviExecutorContextImpl ctx = getExecutorContext();
+        Anchor a = null;
+        if ("src".equals(it.tag)) {
+          a = new Anchor.Source(
+            it.start,
+            ctx.getCommandRange().end(),
+            it.data,
+            it.lineNumber
+          );
+        }
+        anchorSet.add(a);
+      }
+    }
+
+    @Override
+    public void doSpecial(byte [] _xxx)
+    throws DviException
+    {
+      String xxx = new String(_xxx);
+      Matcher mat;
+      DviExecutorContextImpl ctx = getExecutorContext();
+      if ((mat = srcPat.matcher(xxx)).matches()) {
+        flushStack();
+        try {
+          stack.push(
+            new StackItem(
+              ctx.getCommandRange().begin(),
+              "src",
+              mat.group(2), // filename
+              Integer.parseInt(mat.group(1)) // linenumber
+            )
+          );
+        } catch (NumberFormatException e) {
+          LOGGER.warning(e.getMessage());
+        }
+      }
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/Benchmark.java b/src/jp/sourceforge/dvibrowser/dvicore/util/Benchmark.java
new file mode 100644 (file)
index 0000000..eeeed95
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+
+import java.text.MessageFormat;
+
+public class Benchmark {
+  private boolean active = false;
+  private long start = 0;
+  private long end = 0;
+  private long lapsed = 0;
+  private long samples = 0;
+  private String name;
+
+  public void begin(String name)
+  {
+    reset();
+    start = getCurrentTime();
+    this.name = name;
+    active = true;
+  }
+  
+  public void reset()
+  {
+    start = 0;
+    end = 0;
+    lapsed = 0;
+    samples = 0;
+    name = null;
+    active = false;
+  }
+
+  public void addSample()
+  {
+    addSamples(1);
+  }
+  
+  public void addSamples(int count)
+  {
+    samples += count;
+  }
+  
+  public void end()
+  {
+    if (active) {
+      end = getCurrentTime();
+      lapsed = end - start;
+      active = false;
+    }
+  }
+  
+  public long getCurrentTime()
+  {
+    return System.currentTimeMillis();
+  }
+  
+  public long getLapsedTime()
+  {
+    if (active) {
+      long now = getCurrentTime();
+      return now - start;
+    } else {
+      return lapsed;
+    }
+  }
+  
+  public long getSamples()
+  {
+    return samples;
+  }
+
+  public double samplesPerSecond(long samples, long lapsed)
+  {
+    return (double) samples * 1000 / lapsed;
+  }
+
+  public String format(String fmt)
+  {
+    long samples = getSamples();
+    long lapsed = getLapsedTime();
+    return MessageFormat.format(
+             fmt, 
+             samples,
+             (double) lapsed / 1000,
+             samplesPerSecond(samples, lapsed),
+             1./samplesPerSecond(samples, lapsed) * 1000
+           );
+  }
+
+  public String format()
+  {
+    return format("Benchmark result: " + name + ": "
+      + "{0,number} samples in {1,number} sec."
+      + " {2,number} samples/sec. "
+      + " {3,number,##.###} msec./sample.");
+  }
+
+  public String toString()
+  {
+    return format();
+  }
+
+  public long getStartTime() {
+    return start;
+  }
+
+  public long getEndTime() {
+    return end;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/BufferFilter.java b/src/jp/sourceforge/dvibrowser/dvicore/util/BufferFilter.java
new file mode 100644 (file)
index 0000000..e2f7e51
--- /dev/null
@@ -0,0 +1,155 @@
+package jp.sourceforge.dvibrowser.dvicore.util;
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class BufferFilter
+extends InputStream
+{
+  protected ByteArrayInputStream byteArrayInput;
+  private InputStream input;
+  private final int bufferSize;
+  private byte [] buffer;
+  private int len;
+  
+  public BufferFilter(int bufferSize) {
+    this.byteArrayInput = null;
+    this.input = null;
+    this.bufferSize = bufferSize;
+    this.buffer = null;
+    this.len = 0;
+  }
+  
+  public synchronized void beginInput(InputStream in) throws IOException
+  {
+    input = in;
+  }
+  
+  public synchronized void endInput() throws IOException
+  {
+    input = null;
+  }
+  
+  protected synchronized void fill() throws IOException
+  {
+    if (input == null) {
+      throw new IllegalStateException("Input read before beginInput() is called.");
+    }
+    if (byteArrayInput != null && byteArrayInput.available() > 0) {
+      return;
+    }
+    
+    byteArrayInput = fillBuffer();
+  }
+
+  protected synchronized ByteArrayInputStream fillBuffer() throws IOException
+  {
+    byte [] buf = new byte [bufferSize];
+    
+    int len = input.read(buf);
+    if (-1 == len) {
+      throw new IOException("EOF found while reading input.");
+    }
+    if (0 == len) {
+      throw new IOException("Read failed.");
+    }
+    
+    this.len = len;
+    this.buffer = buf;
+    
+    return new ByteArrayInputStream(buf, 0, len);
+  }
+  
+  public synchronized byte [] getBuffer()
+  {
+    if (buffer == null) return new byte[0];
+    byte [] ret = new byte[len];
+    System.arraycopy(buffer, 0, ret, 0, len);
+    return ret;
+  }
+  
+  @Override
+  public synchronized int available() throws IOException
+  {
+    fill();
+    return byteArrayInput.available();
+  }
+  
+  @Override
+  public synchronized void close() throws IOException
+  {
+    input.close();
+  }
+  
+  @Override
+  public void mark(int readLimit)
+  {
+    throw new UnsupportedOperationException("mark is not supported");
+  }
+  
+  @Override
+  public void reset()
+  {
+    throw new UnsupportedOperationException("reset is not supported");
+  }
+  
+  @Override
+  public boolean markSupported()
+  {
+    return false;
+  }
+  
+  @Override
+  public synchronized int read() throws IOException
+  {
+    fill();
+    return byteArrayInput.read();
+  }
+
+  @Override
+  public synchronized int read(byte [] buf, int off, int len) throws IOException
+  {
+    fill();
+    return byteArrayInput.read(buf, off, len);
+  }
+
+  public synchronized InputStream getInput() {
+    return input;
+  }
+
+  public int getBufferSize() {
+    return bufferSize;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/Canonicalizer.java b/src/jp/sourceforge/dvibrowser/dvicore/util/Canonicalizer.java
new file mode 100644 (file)
index 0000000..59d43a9
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+
+public abstract interface Canonicalizer<T>
+{
+  public T canonicalize(T obj);
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/CommandShell.java b/src/jp/sourceforge/dvibrowser/dvicore/util/CommandShell.java
new file mode 100644 (file)
index 0000000..e48f938
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+
+
+public class CommandShell
+{
+  public static final int RETCODE_SUCCESS = 0;
+  
+  private final ScheduledExecutorService exe = Executors.newSingleThreadScheduledExecutor
+    (new DaemonThreadFactory());
+  
+  private static final Logger LOGGER = Logger.getLogger(CommandShell.class.getName());
+  private ArrayList<String> commandLine = new ArrayList<String>();
+
+  public void setCommandLine(String ... commandLine)
+  {
+    ArrayList<String> list = new ArrayList<String>();
+    for (String arg : commandLine) {
+      list.add(arg);
+    }
+    this.commandLine = list;
+  }
+
+  public void setCommandLine(Collection<String> cmdLine)
+  {
+    ArrayList<String> list = new ArrayList<String>();
+    list.addAll(cmdLine);
+    this.commandLine = list;
+  }
+  
+  public Collection<String> getCommandLine()
+  {
+    return Collections.unmodifiableCollection(commandLine);
+  }
+
+  private ArrayList<String> envs = null;
+  public void setEnvironment(Collection<String> envs)
+  {
+    if (envs == null) {
+      this.envs = null;
+      return;
+    } else {
+      ArrayList<String> list = new ArrayList<String>();
+      list.addAll(envs);
+      this.envs = list;
+    }
+  }
+
+  public void setEnvironment(String ... envs)
+  {
+    ArrayList<String> list = new ArrayList<String>();
+    for (String arg : envs) {
+      list.add(arg);
+    }
+    this.envs = list;
+  }
+  
+  public Collection<String> getEnvironment()
+  {
+    if (envs == null) return null;
+    return Collections.unmodifiableCollection(envs);
+  }
+
+  private File dir = null;
+  public void setWorkingDirectory(File dir)
+  {
+    this.dir = dir;
+  }
+  
+  public File getWorkingDirectory()
+  {
+    return dir;
+  }
+  
+  private TimeUnit timeUnit;
+  private long timeout = 0;
+  public void setTimeout(long timeout, TimeUnit timeUnit)
+  {
+    if (timeout < 0) {
+      throw new IllegalArgumentException("timeout is negative");
+    }
+    this.timeout = timeout;
+    this.timeUnit = timeUnit;
+  }
+
+  private CommandShellHandler handler = null;
+  public void setHandler(CommandShellHandler handler)
+  {
+    this.handler = handler;
+  }
+  
+  public CommandShellHandler getHandler()
+  {
+    return handler;
+  }
+
+  protected void checkVars()
+  {
+    if (commandLine == null)
+      throw new IllegalArgumentException
+        ("commandLine can't be null");
+    if (commandLine.size() < 1)
+      throw new IllegalArgumentException
+        ("commandLine can't be empty");
+  }
+
+  private volatile Process p = null;
+  private Thread stdoutThread = null;
+  private Thread stderrThread = null;
+  private Thread stdinThread = null;
+  private volatile Throwable stdoutThrowable = null;
+  private volatile Throwable stderrThrowable = null;
+  private volatile Throwable stdinThrowable = null;
+
+  public int execute()
+  throws IOException, InterruptedException, DviException
+  {
+    int result = -1;
+
+    p = null;
+    checkVars();
+
+    try {
+      final String commandLineStr = DviUtils.join(" ", commandLine);
+      LOGGER.fine("Running command: " + commandLineStr);
+      p = Runtime.getRuntime().exec(
+          commandLine.toArray(new String[commandLine.size()]),
+          getEnvironmentAsArray(), dir);
+      processStreams();
+      ScheduledFuture<?> future = null;
+      if (timeUnit != null && timeout > 0) {
+        LOGGER.fine("Starting timer: timeout=" + timeout + " timeUnit=" + timeUnit);
+        future = exe.schedule(new Runnable() {
+          public void run() {
+            LOGGER.warning("Command timed out: " + commandLineStr);
+            p.destroy();
+          }
+        }, timeout, timeUnit);
+      }
+      LOGGER.fine("waiting for the process to terminate.");
+      result = p.waitFor();
+      LOGGER.fine("process exit with retcode " + result);
+      if (future != null) {
+        future.cancel(false);
+      }
+    } catch (InterruptedException ex) {
+      if (p != null) {
+        p.destroy();
+      }
+      throw ex;
+    } catch (IOException ex) {
+      if (p != null) {
+        p.destroy();
+      }
+      throw ex;
+    } finally {
+      reapThread(stderrThread, "stderr");
+      stderrThread = null;
+      reapThread(stdoutThread, "stdout");
+      stdoutThread = null;
+      reapThread(stdinThread, "stdin");
+      stdinThread = null;
+      
+      if (p != null) {
+        DviUtils.silentClose(p.getErrorStream());
+        DviUtils.silentClose(p.getInputStream());
+        DviUtils.silentClose(p.getOutputStream());
+      }
+    }
+    
+    if (stdinThrowable != null) {
+      throw new DviException(stdinThrowable);
+    }
+    if (stdoutThrowable != null) {
+      throw new DviException(stdoutThrowable);
+    }
+    if (stderrThrowable != null) {
+      throw new DviException(stderrThrowable);
+    }
+    
+    p = null;
+
+    return result;
+  }
+
+  private String[] getEnvironmentAsArray() {
+    if (envs == null) return null;
+    return envs.toArray(new String[envs.size()]);
+  }
+
+  private void reapThread(Thread thread, String name)
+  {
+    try {
+      LOGGER.fine("waiting for the " + name + " thread");
+      if (thread != null)
+        thread.join();
+    } catch (InterruptedException ex) {
+      DviUtils.logStackTrace(LOGGER, Level.WARNING, ex);
+    }
+  }
+
+  protected void processStreams()
+  throws IOException
+  {
+    if (handler != null) {
+      stdinThread = new Thread(new Runnable() {
+        public void run() {
+          OutputStream os = p.getOutputStream();
+          try {
+            handler.handleStdin(os);
+          } catch (Throwable ex) {
+            DviUtils.logStackTrace(LOGGER, Level.WARNING, ex);
+            stdinThrowable = ex;
+            p.destroy();
+          } finally {
+            DviUtils.silentClose(os);
+          }
+        }
+      });
+      stderrThread = new Thread(new Runnable() {
+        public void run() {
+          InputStream is = p.getErrorStream();
+          try {
+            handler.handleStderr(is);
+          } catch (Throwable ex) {
+            DviUtils.logStackTrace(LOGGER, Level.WARNING, ex);
+            stderrThrowable = ex;
+            p.destroy();
+          } finally {
+            DviUtils.silentClose(is);
+          }
+        }
+      });
+      stdoutThread = new Thread(new Runnable() {
+        public void run() {
+          InputStream is = p.getInputStream();
+          try {
+            handler.handleStdout(is);
+          } catch (Throwable ex) {
+            DviUtils.logStackTrace(LOGGER, Level.WARNING, ex);
+            stdoutThrowable = ex;
+            p.destroy();
+          } finally {
+            DviUtils.silentClose(is);
+          }
+        }
+      });
+
+      stdinThread .start();
+      stderrThread.start();
+      stdoutThread.start();
+    } else {
+      DviUtils.silentClose(p.getOutputStream());
+      DviUtils.silentClose(p.getInputStream());
+      DviUtils.silentClose(p.getErrorStream());
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/CommandShellHandler.java b/src/jp/sourceforge/dvibrowser/dvicore/util/CommandShellHandler.java
new file mode 100644 (file)
index 0000000..507b4d3
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+
+public interface CommandShellHandler
+{
+  public void handleStdin(OutputStream out) throws IOException, DviException;
+  public void handleStdout(InputStream in) throws IOException, DviException;
+  public void handleStderr(InputStream in) throws IOException, DviException;
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/DaemonThreadFactory.java b/src/jp/sourceforge/dvibrowser/dvicore/util/DaemonThreadFactory.java
new file mode 100644 (file)
index 0000000..843be23
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+
+import java.util.concurrent.ThreadFactory;
+
+public class DaemonThreadFactory
+implements ThreadFactory
+{
+  public DaemonThreadFactory()
+  {
+    this(Thread.NORM_PRIORITY);
+  }
+  
+  private final int priority;
+  public DaemonThreadFactory(int priority)
+  {
+    this.priority = priority;
+  }
+  
+  public Thread newThread(Runnable r)
+  {
+    Thread t = new Thread(r);
+    t.setPriority(priority);
+    t.setDaemon(true);
+    return t;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/DefaultCommandShellHandler.java b/src/jp/sourceforge/dvibrowser/dvicore/util/DefaultCommandShellHandler.java
new file mode 100644 (file)
index 0000000..e48bc9f
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+public class DefaultCommandShellHandler implements CommandShellHandler {
+//  private static final Logger LOGGER = Logger
+//      .getLogger(CommandShellHandler.class.getName());
+  
+  private final LineBuffer<String> stderr = new LineBuffer<String>(100);
+  private final LineBuffer<String> stdout = new LineBuffer<String>(100);
+  
+  public void handleStderr(InputStream in) throws IOException {
+    DviUtils.accumulateToLineBuffer(in, stderr);
+  }
+
+  public void handleStdin(OutputStream out) throws IOException {
+    out.close();
+  }
+
+  public void handleStdout(InputStream in) throws IOException {
+    DviUtils.accumulateToLineBuffer(in, stdout);
+  }
+
+  public List<String> getStderr() {
+    return stderr.toList();
+  }
+
+  public List<String> getStdout() {
+    return stdout.toList();
+  }
+  
+  
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/DumpCommandShellHandler.java b/src/jp/sourceforge/dvibrowser/dvicore/util/DumpCommandShellHandler.java
new file mode 100644 (file)
index 0000000..edb0fee
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+public class DumpCommandShellHandler
+implements CommandShellHandler
+{
+  private final PrintStream out;
+
+  public DumpCommandShellHandler(PrintStream out)
+  {
+    this.out = out;
+  }
+  
+  public void handleStdout(InputStream in) throws IOException
+  {
+    dumpStream("stdout", in);
+  }
+  public void handleStderr(InputStream in) throws IOException
+  {
+    dumpStream("stderr", in);
+  }
+
+  private void dumpStream(String prefix, InputStream in)
+  throws IOException
+  {
+    BufferedReader r = new BufferedReader
+      (new InputStreamReader(in));
+    String line;
+    while (null != (line = r.readLine())) {
+      out.println("[" + prefix + "] " + line);
+    }
+  }
+
+  public void handleStdin(OutputStream out) throws IOException {
+    out.close();
+  }
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/DviCache.java b/src/jp/sourceforge/dvibrowser/dvicore/util/DviCache.java
new file mode 100644 (file)
index 0000000..a003d19
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+public class DviCache<K, V>
+extends LinkedHashMap<K, V>
+{
+  private static final Logger LOGGER = Logger.getLogger(DviCache.class.getName());
+  private static final long serialVersionUID = -2767494208998923925L;
+
+  private int cacheSize;
+  
+  public DviCache(int cacheSize) {
+    this.cacheSize = cacheSize;
+  }
+  
+  public DviCache() { this(128); }
+  
+  @Override
+  protected boolean removeEldestEntry(Map.Entry<K, V> entry)
+  {
+    if (size() > cacheSize) {
+      LOGGER.finer("purge: key=" + entry.getKey() + " value=" + entry.getValue());
+      return true;
+    }
+    return false;
+  }
+
+  public int getCacheSize()
+  {
+    return cacheSize;
+  }
+
+  public void setCacheSize(int cacheSize)
+  {
+    this.cacheSize = cacheSize;
+  }
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/DviDesktop.java b/src/jp/sourceforge/dvibrowser/dvicore/util/DviDesktop.java
new file mode 100644 (file)
index 0000000..4f23541
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.logging.Logger;
+
+public class DviDesktop {
+  private static final Logger LOGGER = Logger.getLogger(DviDesktop.class
+      .getName());
+  
+  private static final DviDesktop instance = new DviDesktop();
+  
+  private DviDesktop()
+  {
+  }
+  
+  public static DviDesktop getDesktop()
+  {
+    return instance;
+  }
+  
+  public static boolean isDesktopSupported()
+  {
+    if (DviUtils.isMacOSX()) {
+      return true;
+    } else if (DviUtils.isWindows()) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+  
+  public void browse(URI uri)
+  throws IOException
+  {
+    throw new UnsupportedOperationException();
+  }
+
+  public void open(File file)
+  throws IOException
+  {
+    if (DviUtils.isMacOSX()) {
+      try {
+        Class<?> cls = Class.forName("com.apple.eio.FileManager");
+        Method openURL = cls.getDeclaredMethod("openURL",
+            new Class[] { String.class });
+        openURL.invoke(null, new Object[] { file.toURL().toExternalForm() });
+      } catch (SecurityException e) {
+        LOGGER.warning(e.toString());
+      } catch (IllegalArgumentException e) {
+        LOGGER.warning(e.toString());
+      } catch (ClassNotFoundException e) {
+        LOGGER.warning(e.toString());
+      } catch (NoSuchMethodException e) {
+        LOGGER.warning(e.toString());
+      } catch (IllegalAccessException e) {
+        LOGGER.warning(e.toString());
+      } catch (InvocationTargetException e) {
+        LOGGER.warning(e.toString());
+      }
+    } else if (DviUtils.isWindows()) {
+      String[] commandLine = { "cmd.exe", "/c", "start", file.getAbsolutePath() };
+      try {
+        Process p = Runtime.getRuntime().exec(commandLine, null, null);
+        int ret = p.waitFor();
+        LOGGER.fine("Process exit with retcode: " + ret);
+      } catch (InterruptedException e) {
+        LOGGER.warning(e.toString());
+      }
+    } else {
+      LOGGER.severe("This platform does not support file association.");
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/DviInfoDumper.java b/src/jp/sourceforge/dvibrowser/dvicore/util/DviInfoDumper.java
new file mode 100644 (file)
index 0000000..471fdd6
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+
+
+// TODO: Find out what the purpose of this class is.
+public class DviInfoDumper
+{
+  private final Config conf;
+
+  public DviInfoDumper(Config conf)
+  {
+    this.conf = conf;
+  }
+
+  public void execute()
+  throws DviException
+  {
+    try {
+      System.out.printf("mode=%s\n", this.conf.mode);
+      for (String a : this.conf.plainArgs) {
+        System.out.printf("arg: %s\n", a);
+      }
+//    } catch (DviException ex) {
+//      throw ex;
+    } catch (Exception ex) {
+      throw new DviException(ex);
+    }
+  }
+
+  public static class ArgumentList
+  extends LinkedList<String>
+  {
+       private static final long serialVersionUID = -9222800531977990881L;
+
+       public ArgumentList(String [] args)
+    {
+      for (String a : args) add(a);
+    }
+
+    public String next()
+    {
+      if (size() == 0) return null;
+
+      return removeFirst();
+    }
+  }
+
+  @SuppressWarnings("unused")
+  public static class Option
+  {
+    private final String longOpt;
+    private final String shortOpt;
+    private final int numArgs;
+       private final String var;
+    private final String description;
+
+    public Option(String longOpt, String shortOpt,
+      String var, String description)
+    {
+      this.longOpt = longOpt;
+      this.shortOpt = shortOpt;
+      this.var = var;
+      this.numArgs = (var != null) ? 1 : 0;
+      this.description = description;
+    }
+  }
+  
+  public static class Config
+  {
+    private String mode = "help";
+    private ArrayList<String> plainArgs
+      = new ArrayList<String>();
+
+    private static Option [] options = {
+      new Option("--help", "-h", null,     "Show this help"),
+      new Option("--mode", "-m", "mode", "Set mode to <mode>")
+    };
+
+    public Config()
+    {
+    }
+
+    public void setMode(String mode)
+    {
+      this.mode = mode.toLowerCase();
+    }
+
+    public void addPlainArgument(String a)
+    {
+      plainArgs.add(a);
+    }
+
+    private static final Pattern patShortOption
+      = Pattern.compile("^(-[0-9a-zA-Z])(.*)$");
+    private static final Pattern patLongOption
+      = Pattern.compile("^(--[a-zA-Z][a-zA-Z0-9]*)(=(.*))?$");
+
+    public static Config parseCommandLine(String [] args)
+    throws DviException
+    {
+      ArgumentList al = new ArgumentList(args);
+      Config conf = new Config();
+      String a;
+      while (null != (a = al.next())) {
+        if (a.startsWith("-")) {
+          Matcher mat;
+          boolean handled = false;
+
+          if ((mat = patLongOption.matcher(a)).find()) {
+            String a2 = mat.group(1);
+            String v = mat.group(3);
+            System.out.println("found long option: " + a2 + "=>" + v);
+            for (Option opt : options) {
+              if (a2.equals(opt.longOpt)) {
+                handled = true;
+                if (opt.numArgs > 0) {
+                  if (v == null)
+                    throw new DviException
+                      ("no value for option `" + a2 + "'");
+                } else {
+                  if (v != null)
+                    throw new DviException
+                      ("option `" + a2 + "' does not take argument");
+                }
+                break;
+              }
+            }
+          } else if ((mat = patShortOption.matcher(a)).find()) {
+            String a2 = mat.group(1);
+            String v = mat.group(3);
+            System.out.println("found short option: " + a2 + "=>" + v);
+            for (Option opt : options) {
+              if (a.equals(opt.shortOpt)) {
+                handled = true;
+                break;
+              }
+            }
+          }
+
+          if (!handled) {
+            throw new DviException
+              ("unrecognized option: " + a);
+          }
+        } else {
+          conf.addPlainArgument(a);
+        }
+      }
+      return conf;
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/DviUtils.java b/src/jp/sourceforge/dvibrowser/dvicore/util/DviUtils.java
new file mode 100644 (file)
index 0000000..b5266df
--- /dev/null
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.CRC32;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+
+
+public class DviUtils {
+  private static final Logger LOGGER = Logger.getLogger(DviUtils.class
+      .getName());
+  private static final String OS_NAME = System.getProperty("os.name");
+  
+  private static final UUID applicationInstanceUUID = UUID.randomUUID();
+  private static final AtomicLong serializer = new AtomicLong();
+  
+  public static long generateSerialNumber()
+  {
+    return serializer.incrementAndGet();
+  }
+  
+  public static UUID getApplicationInstanceUUID()
+  {
+    return applicationInstanceUUID;
+  }
+  
+  public static String generateUniqueId()
+  {
+    String uniqueId = getApplicationInstanceUUID().toString() + "--" +  generateSerialNumber();
+    return uniqueId;
+  }
+  
+  private DviUtils() {}
+  
+  public static boolean isFile(URL url)
+  {
+    if (url == null) return false;
+    return "file".equals(url.getProtocol());
+  }
+
+  public static void writeStringToFile(File file, String data)
+  throws IOException
+  {
+    FileOutputStream fos = null;
+    try {
+      fos = new FileOutputStream(file);
+      // TODO: support encodings
+      byte[] buf = data.getBytes();
+      fos.write(buf);
+    } finally {
+      if (fos != null) {
+        fos.flush();
+        silentClose(fos);
+      }
+    }
+  }
+  
+  public static File toLocalFile(URL url)
+  throws DviException
+  {
+    if (url == null) return null;
+    if ("file".equals(url.getProtocol())) {
+      return new File(url.getPath());
+    } else {
+      throw new DviException("URL does not point to a local file. " + url);
+    }
+  }
+  
+  public static String md5Hex(String ps)
+  {
+    if (ps == null) return null;
+    try {
+      MessageDigest md = MessageDigest.getInstance("MD5");
+      // TODO: support character encodings.
+      byte[] digest = md.digest(ps.getBytes());
+      StringBuilder sb = new StringBuilder();
+      for (byte b : digest) {
+        sb.append(String.format("%02x", b));
+      }
+      return sb.toString();
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+    return "0";
+  }
+  
+  private static final Pattern patEnv = Pattern.compile("^([^=]*)=(.*)$");
+
+  public static Map<String, String> getLoginShellEnvPosix() throws IOException, InterruptedException
+  {
+    return getLoginShellEnvPosix(null);
+  }
+  
+  public static Map<String, String> getLoginShellEnvPosix(String shell)
+      throws IOException, InterruptedException {
+    String[] cmds = new String[] { "printenv" };
+    if (shell != null) {
+      cmds = new String[] { shell, "-l", "-c", "printenv" };
+    }
+
+    final Process proc = Runtime.getRuntime().exec(cmds);
+    final Map<String, String> map = new HashMap<String, String>();
+    final InputStream is = proc.getInputStream();
+    proc.getOutputStream().close();
+    proc.getErrorStream().close();
+    {
+      Scanner s = new Scanner(proc.getInputStream());
+      try {
+        while (s.hasNext()) {
+          String line = s.nextLine();
+          Matcher mat = patEnv.matcher(line);
+          if (mat.matches()) {
+            String key = mat.group(1);
+            String value = mat.group(2);
+            LOGGER.finest("key=" + key + " value=" + value);
+            map.put(key, value);
+          }
+        }
+      } finally {
+        DviUtils.silentClose(is);
+      }
+    }
+    proc.waitFor();
+    return map;
+  }
+  
+  public static String getPathByLoginShellPosix() throws IOException, InterruptedException
+  {
+    String path = null;
+    
+    // We first determine the user's shell.
+    Map<String, String> env0 = getLoginShellEnvPosix(null);
+    String shell = env0.get("SHELL");
+    if (shell != null) {
+      // We then read the PATH environment variable through the login shell.
+      Map<String, String> env1 = getLoginShellEnvPosix(shell);
+      path = env1.get("PATH");
+    }
+    
+    return path;
+  }
+  
+  public static String join(String left, String mid, String right, Object [] arg)
+  {
+    if (arg == null) return null;
+    StringBuilder sb = new StringBuilder();
+    left = wrapNull(left, "");
+    mid = wrapNull(mid, "");
+    right = wrapNull(right, "");
+    String sep = left;
+    for (Object o : arg) {
+      sb.append(sep + String.valueOf(o));
+      sep = mid;
+    }
+    sb.append(right);
+    return sb.toString();
+  }
+
+  public static String join(String sep, Object [] args) 
+  {
+    return join(null, sep, null, args);
+  }
+  
+  public static <T> T wrapNull(T value, T defaultValue) {
+    if (value == null) return defaultValue;
+    return value;
+  }
+
+  // This method is unsafe because we don't escape the command line arguments.  Use it with care.
+  public static Process unsafeRunCommandByShell(String[] cmds, String[] envp, File workDir)
+      throws IOException
+  {
+    Process p = null;
+    if (!isWindows()) {
+      try {
+        Map<String, String> env = getLoginShellEnvPosix();
+        String shell = env.get("SHELL");
+        if (shell != null) {
+          p = unsafeRunCommandByShellPosix(shell, true, cmds, envp, workDir);
+        }
+      } catch (InterruptedException e) {
+        LOGGER.warning(e.toString());
+      }
+    }
+    if (p == null) {
+      p = Runtime.getRuntime().exec(cmds, envp, workDir);
+    }
+    return p;
+  }
+  
+  // This method is unsafe because we don't escape the command line arguments.  Use it with care.
+  public static Process unsafeRunCommandByShellPosix(String shell,
+      boolean useLoginShell, String[] cmds, String[] envp, File workDir)
+      throws IOException
+  {
+    ArrayList<String> list = new ArrayList<String>();
+    list.add(shell);
+    if (useLoginShell) {
+      list.add("-l");
+    }
+
+    String[] cmdLine = list.toArray(new String[list.size()]);
+
+    StringBuilder subCommand = new StringBuilder();
+    for (String cmd : cmds) {
+      subCommand.append(cmd + " ");
+    }
+
+    LOGGER.fine("running shell: " + join(" ", cmdLine));
+    LOGGER.fine("running command: " + subCommand.toString());
+
+    Process proc = Runtime.getRuntime().exec(cmdLine, envp, workDir);
+
+    PrintWriter out = new PrintWriter(proc.getOutputStream());
+    out.println(subCommand.toString());
+    out.close();
+
+    return proc;
+  }
+
+  public static Thread dumpStreamAsync(final String name, final InputStream is,
+      final PrintStream out) {
+    Thread t = new Thread(new Runnable() {
+      public void run() {
+        Scanner s = new Scanner(is);
+        try {
+          while (s.hasNext()) {
+            String line = s.nextLine();
+            if (out != null) {
+              out.println("[" + name + "] " + line);
+            }
+            if (LOGGER.isLoggable(Level.FINER)) {
+              LOGGER.finer("[" + name + "] " + line);
+            }
+          }
+        } finally {
+          try {
+            s.close();
+          } catch (Exception e) {
+          }
+        }
+
+      }
+    });
+    t.start();
+    return t;
+  }
+  
+  public static <T> T head(T[] array) {
+    if (array == null || array.length == 0) return null;
+    return array[0];
+  }
+  
+  public static void logLinesFromStream(final String name, final InputStream is,
+      final Logger logger, final Level level) {
+    if (logger == null || !logger.isLoggable(level))
+      return;
+    Scanner s = new Scanner(is);
+    while (s.hasNext()) {
+      String line = s.nextLine();
+      LOGGER.log(level, "[" + name + "] " + line);
+    }
+  }
+  
+  public static void addLinesFromStream(final List<String> output, final InputStream is) {
+    addLinesFromStream(output, is, null, Level.SEVERE, null);
+  }
+  
+  public static void addLinesFromStream(final List<String> output, final InputStream is, final Logger logger, final Level level, final PrintStream out) {
+    if (output == null) return;
+    Scanner s = new Scanner(is);
+    while (s.hasNext()) {
+      String line = s.nextLine();
+      output.add(line);
+      if (logger != null && logger.isLoggable(level)) {
+        logger.log(level, line);
+      }
+      if (out != null) {
+        out.println(line);
+        out.flush();
+      }
+    }
+  }
+
+  
+  public static Thread dumpStreamAsync(final String name, final InputStream is,
+      final Logger logger, final Level level) {
+    Thread t = new Thread(new Runnable() {
+      public void run() {
+        try {
+          logLinesFromStream(name, is, logger, level);
+        } finally {
+          silentClose(is);
+        }
+      }
+    });
+    t.start();
+    return t;
+  }
+
+
+  public static boolean isMacOSX() {
+    return OS_NAME.startsWith("Mac OS X");
+  }
+
+  public static boolean isWindows() {
+    return OS_NAME.startsWith("Windows");
+  }
+
+  public static void silentClose(Closeable s) {
+    if (s == null) return;
+    try {
+      s.close();
+    } catch (Exception ex) {
+      logStackTrace(LOGGER, Level.WARNING, ex);
+    }
+  }
+
+  public static byte[] readFileToByteArray(File file) throws IOException {
+    if (file == null)
+      return null;
+    int len = (int) file.length();
+    FileInputStream is = new FileInputStream(file);
+    try {
+      byte[] buf = new byte[len];
+      if (len != is.read(buf)) {
+        throw new IOException("Failed to read file content");
+      }
+      return buf;
+    } finally {
+      silentClose(is);
+    }
+  }
+
+  public static String readFileToString(File file, String encoding)
+      throws IOException {
+    byte[] buf = readFileToByteArray(file);
+    if (buf != null) {
+      return new String(buf, encoding);
+    }
+
+    return null;
+  }
+
+  public static String readFileToString(File file) throws IOException {
+    byte[] buf = readFileToByteArray(file);
+    if (buf != null) {
+      return new String(buf);
+    }
+
+    return null;
+  }
+  
+  public static String [] readLinesFromFile(File file)
+  throws IOException
+  {
+    FileInputStream is = new FileInputStream(file);
+    return readLinesFromStream(is);
+  }
+  
+  public static String [] readLinesFromStream(InputStream is)
+  throws IOException
+  {
+    ArrayList<String> list = new ArrayList<String>();
+    Scanner s = new Scanner(is);
+    try {
+      while (s.hasNext()) {
+        String line = s.nextLine();
+        list.add(line);
+      }
+      return list.toArray(new String[list.size()]);
+    } finally {
+      silentClose(is);
+    }
+  }
+
+
+  public static String [] removeComments(String [] list, String lineCommentStart)
+  {
+    if (list == null) return list;
+
+    ArrayList<String> out = new ArrayList<String>();
+    for (String item : list) {
+      int pos = item.indexOf(lineCommentStart);
+      String line = item;
+      if (pos != -1) {
+        line = line.substring(0, pos);
+      }
+      line = line.trim();
+      if (line.length() > 0) {
+        out.add(line);
+      }
+    }
+    return out.toArray(new String[out.size()]);
+  }
+  
+  public static String joinPath(String [] args) {
+    if (args == null) return null;
+    return join(File.pathSeparator, args);
+  }
+  
+  public static void logStackTrace(Logger logger, Level level, Throwable t)
+  {
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+    pw.println(t.toString());
+    for (StackTraceElement e : t.getStackTrace()) {
+      pw.println
+        ( "at " + e.getClassName()
+        + "." + e.getMethodName()
+        + "(" + e.getFileName()
+        + ":" + e.getLineNumber()
+        + ")"
+        );
+    }
+    logger.log(level, sw.toString());
+  }
+
+  public static File toFile(URL url) {
+    if (url == null) return null;
+    if (!isFile(url)) {
+      throw new IllegalArgumentException("URL does not point to a local file: " + url);
+    }
+    return new File(url.getPath());
+  }
+  
+  public static boolean nullSafeEquals(Object o1, Object o2)
+  {
+    if (o1 == null) return o2 == null;
+    return o1.equals(o2);
+  }
+
+  public static String join(String sep, Collection<String> list) {
+    if (list == null) return null;
+    return join(sep, list.toArray(new String[list.size()]));
+  }
+
+  public static void accumulateToLineBuffer(InputStream in,
+      LineBuffer<String> out) {
+    try {
+      Scanner s = new Scanner(in);
+      while (s.hasNext()) {
+        String line = s.nextLine();
+        out.append(line);
+      }
+    } finally {
+      DviUtils.silentClose(in);
+    }
+  }
+
+  public static String getTimeId() {
+    Calendar cal = Calendar.getInstance();
+    SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
+    return df.format(cal.getTime());
+  }
+
+  public static CRC32 getCRC32(InputStream is) throws IOException
+  {
+    try {
+      byte [] buf = new byte [1024];
+      int len;
+      CRC32 crc = new CRC32();
+      while (-1 != (len = is.read(buf))) {
+        crc.update(buf, 0, len);
+      }
+      return crc;
+    } finally {
+      DviUtils.silentClose(is);
+    }
+  }
+  
+  public static void copyStream(InputStream is, OutputStream os) throws IOException
+  {
+    try {
+      byte [] buf = new byte [1024];
+      int len;
+      while (-1 != (len = is.read(buf))) {
+        os.write(buf, 0, len);
+      }
+    } finally {
+      // We don't close os.  This is intended for use with ZipOutputStream.
+      DviUtils.silentClose(is);
+    }
+  }
+  
+  public String getZipPath(File file)
+  {
+    if (file == null) return null;
+    return file.getPath().replace('\\', '/');
+  }
+
+  public static String hexDump(String unicode) {
+    byte [] bytes = unicode.getBytes();
+    StringBuilder sb = new StringBuilder();
+    for (byte b : bytes) {
+      sb.append(String.format("%02x", b));
+    }
+    return sb.toString();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/LineBuffer.java b/src/jp/sourceforge/dvibrowser/dvicore/util/LineBuffer.java
new file mode 100644 (file)
index 0000000..e0cb144
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+public class LineBuffer<T> {
+  public static final int BUFFER_UNLIMITED = -1;
+  private int maxLineNumbers;
+  private final ArrayList<T> list = new ArrayList<T>();
+  
+  public LineBuffer()
+  {
+    this(BUFFER_UNLIMITED);
+  }
+  
+  public LineBuffer(int maxLineNumbers)
+  {
+    this.setMaxLineNumbers(maxLineNumbers);
+  }
+  
+  public void append(T line)
+  {
+    list.add(line);
+    if (isBufferLimited()) {
+      while (list.size() > maxLineNumbers) {
+        list.remove(0);
+      }
+    }
+  }
+  
+  private boolean isBufferLimited() {
+    return !(maxLineNumbers == BUFFER_UNLIMITED);
+  }
+
+  public List<T> toList() {
+    return Collections.unmodifiableList(list);
+  }
+
+  public int getMaxLineNumbers() {
+    return maxLineNumbers;
+  }
+
+  public void setMaxLineNumbers(int maxLineNumbers) {
+    if (maxLineNumbers < 0) {
+      this.maxLineNumbers = BUFFER_UNLIMITED;
+    } else {
+      this.maxLineNumbers = maxLineNumbers;
+    }
+  }
+
+  public String toString()
+  {
+    StringBuilder sb = new StringBuilder();
+    sb.append(getClass().getName()
+      + "["
+      + "maxLineNumbers=" + maxLineNumbers
+      + "numLines=" + list.size()
+      + "lines=" + DviUtils.join("[", ",", "]", list.toArray())
+      + "]"
+      );
+    return sb.toString();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/SimpleCanonicalizer.java b/src/jp/sourceforge/dvibrowser/dvicore/util/SimpleCanonicalizer.java
new file mode 100644 (file)
index 0000000..be6ff05
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public class SimpleCanonicalizer<T>
+implements Canonicalizer<T>
+{
+  private final Map<T,T> cache = new HashMap<T,T>();
+
+  public T canonicalize(T obj)
+  {
+    if (cache.containsKey(obj)) {
+      return cache.get(obj);
+    } else {
+      cache.put(obj, obj);
+      return obj;
+    }
+  }
+
+  public int cacheSize()
+  {
+    return cache.size();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/TeXMessageParser.java b/src/jp/sourceforge/dvibrowser/dvicore/util/TeXMessageParser.java
new file mode 100644 (file)
index 0000000..3abe1c8
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util;
+
+import java.io.BufferedReader;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+
+
+
+// TODO: support logging
+// TODO: remove this class
+@Deprecated
+public class TeXMessageParser
+{
+  public static final String STATE_INITIAL = "INITIAL";
+
+  public void parse(Reader r, Handler handler)
+  throws DviException, IOException
+  {
+    if (handler == null)
+      throw new NullPointerException
+        ("handler can't be null");
+
+    Env env = new Env();
+
+//    env.parser  = this;
+    env.reader  = new BufferedReader(r);
+    env.handler = handler;
+    env.state   = STATE_INITIAL;
+    env.stop    = false;
+
+    env.firstLine = env.reader.readLine();
+    if (env.firstLine == null)
+      throw new EOFException
+        ("input stream ended while reading the first line.");
+    env.handler.begin(env);
+
+    while (!env.stop) {
+      String line = env.reader.readLine();
+      parseLine(env, line);
+    }
+  }
+
+  protected void parseLine(Env env, String line)
+  throws DviException, IOException
+  {
+    if (line == null) {
+      if (!env.stop) {
+        env.handler.end(env);
+      }
+      env.stop = true;
+      return;
+    }
+
+    env.handler.onEachLine(env, line);
+
+    if (STATE_INITIAL.equals(env.state))  {
+      parseLineInitial(env, line);
+    } else {
+      throw new IllegalStateException();
+    }
+  }
+
+  protected void parseLineInitial(Env env, String line)
+  throws DviException, IOException
+  {
+    env.unrecognized = "";
+    int pos = 0;
+    while (pos < line.length()) {
+      int next = -1;
+      if (line.charAt(pos) == '(') {
+        int p = -1;
+        int p1 = line.indexOf("(", pos+1);
+        int p2 = line.indexOf(")", pos+1);
+        if (p1 == -1)      p = p2;
+        else if (p2 == -1) p = p1;
+        else               p = Math.min(p1, p2);
+        if (p == -1) p = line.length();
+
+        String filename = line.substring(pos+1, p);
+
+        boolean success = env.handler.isSupportedFileName(env, filename);
+//        System.out.println("file: " + filename + ": exists=" + success);
+        if (success) {
+          env.handler.beginInput(env, filename);
+          next = p;
+        }
+      } else if (line.charAt(pos) == ')') {
+        flushUnrecognized(env);
+        env.handler.endInput(env);
+        next = pos+1;
+      } else if (line.charAt(pos) == '[') {
+        String a = line.substring(pos+1);
+        Matcher mat = Pattern.compile("^(\\d+)").matcher(a);
+        if (mat.find()) {
+          int count = -1;
+          try {
+            count = Integer.parseInt(mat.group(1));
+          } catch (NumberFormatException e) {
+          }
+          flushUnrecognized(env);
+          env.handler.setCounter(env, count);
+          next = pos + 1 + mat.group(1).length();
+        }
+      } else if (line.charAt(pos) == ']') {
+        next = pos+1;
+      }
+
+      if (next == -1) {
+        env.unrecognized += line.charAt(pos);
+        pos++;
+      } else {
+        pos = next;
+      }
+    }
+    flushUnrecognized(env);
+  }
+
+  protected void flushUnrecognized(Env env)
+  {
+    String str = env.unrecognized.trim();
+    env.unrecognized = "";
+    if (str != null && str.length() > 0) {
+      env.handler.handleUnrecognized(env, str);
+    } 
+  }
+
+  public static class Env
+  {
+//    private TeXMessageParser parser;
+    private boolean stop;
+    private String state;
+    private String unrecognized;
+    private BufferedReader reader;
+    private Handler handler;
+    private  String firstLine;
+  }
+
+  public static interface Handler
+  {
+    public boolean isSupportedFileName(Env env, String str);
+    public void begin(Env env);
+    public void end(Env env);
+    public void beginInput(Env env, String filename);
+    public void endInput(Env env);
+    public void setCounter(Env env, int count);
+    public void onEachLine(Env env, String line);
+    public void handleUnrecognized(Env env, String line);
+  }
+
+  public static abstract class Adaptor
+  implements Handler
+  {
+    public boolean isSupportedFileName(Env env, String str) {
+      return false;
+    }
+    public void begin(Env env) {}
+    public void end(Env env) {}
+    public void beginInput(Env env, String filename) {}
+    public void endInput(Env env) {}
+    public void setCounter(Env env, int count) {}
+    public void onEachLine(Env env, String line) {}
+    public void handleUnrecognized(Env env, String line) {}
+  }
+
+  public static class Dumper
+  extends Adaptor
+  {
+    protected final PrintStream out;
+    public Dumper(PrintStream out) {
+      this.out = out;
+    }
+    public boolean isSupportedFileName(Env env, String str) {
+      return new File(str).exists();
+    }
+    public void onEachLine(Env env, String line) {
+      out.println("############" + line + "########");
+    }
+    public void beginInput(Env env, String filename) {
+      out.println("<input filename=\"" + filename + "\">");
+    }
+    public void endInput(Env env) {
+      out.println("</input>");
+    }
+    public void handleUnrecognized(Env env, String str) {
+      out.println("<unrecognized>" + str + "</unrecognized>");
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/ZipBuilder.java b/src/jp/sourceforge/dvibrowser/dvicore/util/ZipBuilder.java
new file mode 100644 (file)
index 0000000..a34b414
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+package jp.sourceforge.dvibrowser.dvicore.util;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.logging.Logger;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+
+public class ZipBuilder
+implements Closeable
+{
+  private static final Logger LOGGER = Logger.getLogger(ZipBuilder.class.getName());
+  
+  private final ZipOutputStream output;
+  private final int method;
+
+  public ZipBuilder(OutputStream out, int method)
+  {
+    if (!isValidMethod(method)) {
+      throw new IllegalArgumentException("Unknown ZIP method: " + method);
+    }
+    this.method = method;
+    this.output = createZipOutputStream(out, method);
+  }
+  
+  public ZipBuilder(OutputStream os)
+  {
+    this(os, ZipOutputStream.DEFLATED);
+  }
+  
+  protected ZipOutputStream createZipOutputStream(OutputStream out, int method)
+  {
+    ZipOutputStream zos = new ZipOutputStream(out);
+    zos.setMethod(method);
+    return zos;
+  }
+  
+  protected ZipEntry createZipEntryFromFile(String entryName, File file) throws FileNotFoundException, IOException
+  {
+    ZipEntry ze = new ZipEntry(entryName);
+    switch (getMethod()) {
+    case ZipOutputStream.STORED:
+      ze.setSize(file.length());
+      CRC32 crc = DviUtils.getCRC32(new FileInputStream(file));
+      ze.setCrc(crc.getValue());
+      break;
+    case ZipOutputStream.DEFLATED:
+      break;
+    default:
+      // We have no chance to reach here since the constructor throws
+      // exception when method is not any one of above.
+      throw new IllegalStateException("Unknown ZIP method: " + getMethod());
+    }
+    return ze;
+  }
+  
+  protected File createTempFile() throws IOException
+  {
+    // TODO: Let a manager to control the temporary file.
+    File tmpFile = File.createTempFile("zip-builder", null);
+    tmpFile.deleteOnExit();
+    return tmpFile;
+  }
+  
+  public void write(String entryName, InputStream is) throws IOException
+  {
+    File tmpFile = createTempFile();
+    try {
+      FileOutputStream fos = new FileOutputStream(tmpFile);
+      DviUtils.copyStream(is, fos);
+      fos.flush();
+      fos.close();
+      is.close();
+      write(entryName, tmpFile);
+    } finally {
+      tmpFile.delete();
+    }
+  }
+  
+  public void write(String entryName, File file) throws IOException
+  {
+    ZipEntry ze = createZipEntryFromFile(entryName, file);
+    output.putNextEntry(ze);
+    DviUtils.copyStream(new FileInputStream(file), output);
+  }
+  
+  public OutputStream openOutputStream(final String entryName) throws IOException
+  {
+    final File tmpFile = createTempFile();
+    try {
+      final boolean [] done = new boolean[1];
+      done[0] = false;
+      FileOutputStream fos = new FileOutputStream(tmpFile);
+      FilterOutputStream os = new FilterOutputStream(fos) {
+        public synchronized void close() throws IOException {
+          // Note that this method might be called multiple times.
+          // So we return quickly when called twice.
+          if (done[0]) {
+            return;
+          }
+          done[0] = true;
+          super.close();
+          try {
+            ZipBuilder.this.write(entryName, tmpFile);
+          } finally {
+            tmpFile.delete();
+          }
+        }
+      };
+      return os;
+    } catch (IOException ex) {
+      tmpFile.delete();
+      throw ex;
+    } catch (RuntimeException ex) {
+      tmpFile.delete();
+      throw ex;
+    } catch (Error ex) {
+      tmpFile.delete();
+      throw ex;
+    }
+  }
+  
+  public static boolean isValidMethod(int method) {
+    if (method == ZipOutputStream.DEFLATED || method == ZipOutputStream.STORED) {
+      return true; 
+    }
+    return false;
+  }
+
+  public ZipOutputStream getOutputStream() {
+    return output;
+  }
+
+  public int getMethod() {
+    return method;
+  }
+
+  public void close() throws IOException {
+    output.close();
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/AbstractComputer.java b/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/AbstractComputer.java
new file mode 100644 (file)
index 0000000..9db28ad
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.concurrent;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+public abstract class AbstractComputer<K, V> implements Computer<K, V> {
+  private static final Logger LOGGER = Logger.getLogger(AbstractComputer.class
+      .getName());
+  
+  public abstract ExecutorService getExecutorService(Computation<K, V> computable);
+  
+  public Future<V> compute(Computation<K, V> computable)
+  {
+    ExecutorService exe = getExecutorService(computable);
+    final Future<V> future = exe.submit(computable);
+    LOGGER.finer("Scheduled computation " + computable + " to executor service " + exe + " with future object " + future);
+    return future;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/BasicComputer.java b/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/BasicComputer.java
new file mode 100644 (file)
index 0000000..82616ff
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.concurrent;
+import java.util.concurrent.ExecutorService;
+
+public class BasicComputer<K, V> extends AbstractComputer<K, V> {
+//  private static final Logger LOGGER = Logger.getLogger(BasicComputer.class
+//      .getName());
+  private final ExecutorService executorService;
+  
+  public BasicComputer(ExecutorService executorService)
+  {
+    this.executorService = executorService;
+  }
+
+  public ExecutorService getExecutorService()
+  {
+    return executorService;
+  }
+
+  @Override
+  public ExecutorService getExecutorService(Computation<K, V> computable)
+  {
+    return executorService;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/CacheEntry.java b/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/CacheEntry.java
new file mode 100644 (file)
index 0000000..8c56039
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.concurrent;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class CacheEntry<K, V> {
+//  private static final Logger LOGGER = Logger.getLogger(CachedItem.class
+//      .getName());
+  private final Computer<K, V> cachedComputer;
+  private final Computation<K, V> computable;
+  private final Future<V> future;
+  private final long serialNumber;
+  private static final AtomicLong serializer = new AtomicLong();
+  
+  public CacheEntry(Computer<K, V> cachedComputer, Computation<K, V> computable, Future<V> future)
+  {
+    this.cachedComputer = cachedComputer;
+    this.computable = computable;
+    this.future = future;
+    this.serialNumber = serializer.incrementAndGet();
+  }
+
+  public Computer<K, V> getCachedComputer()
+  {
+    return cachedComputer;
+  }
+
+  public Computation<K, V> getComputable()
+  {
+    return computable;
+  }
+
+  public Future<V> getFuture()
+  {
+    return future;
+  }
+
+  public long getSerialNumber()
+  {
+    return serialNumber;
+  }
+  
+  public String toString()
+  {
+    return getClass().getName()
+      + "["
+      + "serialNumber=" + serialNumber
+      + ",key=" + computable.getCacheKey()
+      + ",future=" + future
+      + "]";
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/Cacheable.java b/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/Cacheable.java
new file mode 100644 (file)
index 0000000..9024537
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.concurrent;
+
+public interface Cacheable<K> {
+  K getCacheKey();
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/CachedComputer.java b/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/CachedComputer.java
new file mode 100644 (file)
index 0000000..9923d28
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.concurrent;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+
+
+public class CachedComputer<K, V> implements Computer<K, V>
+{
+  private final class LinkedHashMapExtension extends
+      LinkedHashMap<K, CacheEntry<K, V>> {
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    protected boolean removeEldestEntry(Map.Entry<K,CacheEntry<K, V>> eldest)
+    {
+      boolean doRemove = CachedComputer.this.removeEldestEntry(eldest);
+      LOGGER.finer("doRemove = " + doRemove);
+      return doRemove;
+    }
+  }
+
+  private static final Logger LOGGER = Logger.getLogger(CachedComputer.class.getName());
+  private final Map<K, CacheEntry<K, V>> map;
+  private final Computer<K, V> computer;
+  
+  public CachedComputer(final Computer<K, V> computer)
+  {
+    this.computer = computer;
+    map = Collections.synchronizedMap(createCache());
+  }
+  
+  protected Map<K, CacheEntry<K, V>> createCache()
+  {
+    return new LinkedHashMapExtension();
+  }
+  
+  protected boolean removeEldestEntry(Map.Entry<K, CacheEntry<K, V>> entry)
+  {
+    return false;
+  }
+  
+  protected synchronized Future<V> startComputation(final Computation<K, V> computable)
+  {
+    final Future<V> future = computer.compute(new Computation<K, V>() {
+      public V call() throws Exception {
+        try {
+          return computable.call();
+        } catch (Throwable e) {
+          // We would like to associate any exception with the return value of null.
+          DviUtils.logStackTrace(LOGGER, Level.SEVERE, e);
+          return null;
+        }
+      }
+
+      public K getCacheKey() {
+        return computable.getCacheKey();
+      }
+      
+    });
+    return future;
+  }
+
+  /* (non-Javadoc)
+   * @see Computer#compute(Computation, java.util.concurrent.ExecutorService)
+   */
+  public synchronized Future<V> compute(Computation<K, V> computable)
+  {
+    K key = computable.getCacheKey();
+    if (key == null) {
+      LOGGER.finer("Key is null: " + computable);
+      final Future<V> future = startComputation(computable);
+      return future;
+    }
+    CacheEntry<K, V> cacheEntry = map.get(key);
+    if (cacheEntry == null) {
+      LOGGER.finer("Cache miss: " + key);
+      final Future<V> future = startComputation(computable);
+      cacheEntry = new CacheEntry<K, V>(this, computable, future);
+      getCache().put(key, cacheEntry);
+      LOGGER.finer("Wrote cache: key=" + key + " value=" + future);
+    } else {
+      LOGGER.finest("Cache hit: " + key);
+    }
+    return cacheEntry.getFuture();
+  }
+
+  public synchronized Future<V> getCachedResult(Computation<K, V> computable)
+  {
+    K key = computable.getCacheKey();
+    if (key == null) {
+      return null;
+    }
+    CacheEntry<K, V> cacheEntry = map.get(key);
+    if (cacheEntry == null) {
+      return null;
+    } else {
+      return cacheEntry.getFuture();
+    }
+  }
+  
+  public Map<K, CacheEntry<K, V>> getCache()
+  {
+    return map;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/Computation.java b/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/Computation.java
new file mode 100644 (file)
index 0000000..68855fc
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.concurrent;
+import java.util.concurrent.Callable;
+
+
+public interface Computation<K, V> extends Callable<V>, Cacheable<K>
+{
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/Computer.java b/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/Computer.java
new file mode 100644 (file)
index 0000000..14171e1
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.concurrent;
+import java.util.concurrent.Future;
+
+public interface Computer<K, V> {
+  Future<V> compute(Computation<K, V> computable);
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/ComputerProgressMonitor.java b/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/ComputerProgressMonitor.java
new file mode 100644 (file)
index 0000000..94e4d4b
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.concurrent;
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+public class ComputerProgressMonitor<K, V> implements Computer<K,V> {
+  private static final Logger LOGGER = Logger.getLogger(ComputerProgressMonitor.class.getName());
+  private final Computer<K, V> computer;
+  
+  public ComputerProgressMonitor(Computer<K, V> computer)
+  {
+    this.computer = computer;
+  }
+  
+  public Future<V> compute(final Computation<K, V> computation)
+  {
+    final Computation<K, V> wrapper = new Computation<K, V>() {
+      public V call() throws Exception
+      {
+        fireComputationBeginEvent(computation);
+        try {
+          V v = computation.call();
+          return v;
+        } catch (Exception e) {
+          throw e;
+        } finally {
+          fireComputationEndEvent(computation);
+        }
+      }
+
+      public K getCacheKey()
+      {
+        return computation.getCacheKey();
+      }
+      
+    };
+    return computer.compute(wrapper);
+  }
+
+  protected void fireComputationEndEvent(Computation<K, V> computation)
+  {
+    LOGGER.finer("computation ended: " + computation);
+  }
+
+  protected void fireComputationBeginEvent(Computation<K, V> computation)
+  {
+    LOGGER.finer("computation started: " + computation);
+  }
+
+  public Computer<K, V> getComputer()
+  {
+    return computer;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/ThreadedComputer.java b/src/jp/sourceforge/dvibrowser/dvicore/util/concurrent/ThreadedComputer.java
new file mode 100644 (file)
index 0000000..8c9c9a5
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.concurrent;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import jp.sourceforge.dvibrowser.dvicore.util.DaemonThreadFactory;
+
+
+public class ThreadedComputer<K, V> extends AbstractComputer<K, V> {
+//  private static final Logger LOGGER = Logger.getLogger(BasicComputer.class
+//      .getName());
+  private final ExecutorService executorService;
+  
+  public ThreadedComputer(int nThreads, ThreadFactory threadFactory)
+  {
+    if (threadFactory == null) {
+      threadFactory = createThreadFactory();
+    }
+    executorService = createExecutorService(nThreads, threadFactory);
+  }
+
+  public ThreadedComputer(int nThreads)
+  {
+    this(nThreads, null);
+  }
+  
+  protected ThreadFactory createThreadFactory()
+  {
+    return new DaemonThreadFactory(Thread.MIN_PRIORITY);
+  }
+  
+  protected ExecutorService createExecutorService(int nThreads, ThreadFactory threadFactory)
+  {
+    return Executors.newFixedThreadPool(nThreads, threadFactory);
+  }
+
+  public ExecutorService getExecutorService()
+  {
+    return executorService;
+  }
+
+  @Override
+  public ExecutorService getExecutorService(Computation<K, V> computable)
+  {
+    return executorService;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvCellCodec.java b/src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvCellCodec.java
new file mode 100644 (file)
index 0000000..4a5268d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.csv;
+public interface CsvCellCodec<K, V>
+{
+  public String encodeKey(K key);
+  public String encodeValue(K key, V value);
+  public K decodeKey(String s);
+  public V decodeValue(K key, String s);
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvData.java b/src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvData.java
new file mode 100644 (file)
index 0000000..f3d9f9c
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.csv;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+
+public class CsvData<K, V> {
+  private static final Logger LOGGER = Logger.getLogger(CsvData.class
+      .getName());
+  private static final String DEFAULT_QUOTE_CHAR = "\"";
+  private static final String DEFAULT_SEPARATOR = ",";
+  
+  private final List<Map<K, V>> lines = new ArrayList<Map<K, V>>();
+  private final List<K> defaultKeys = new ArrayList<K>();
+  private final CsvCellCodec<K, V> codec;
+  
+  public CsvData(CsvCellCodec<K, V> codec)
+  {
+    if (codec == null)
+      throw new IllegalArgumentException("CSV cell codec can't be null");
+    this.codec = codec;
+  }
+  
+  private Map<K, V> line;
+  
+  public void beginLine()
+  {
+    if (line != null) {
+      throw new IllegalStateException("line is not null: last line is not closed.");
+    }
+    
+    line = newLine();
+  }
+
+  protected Map<K, V> newLine() {
+    return new HashMap<K, V>();
+  }
+  
+  public void endLine()
+  {
+    lines.add(line);
+    line = null;
+  }
+  
+  public void put(K key, V value)
+  {
+    if (line == null) {
+      throw new IllegalStateException("line is null: put() method is called before beginLine() is invoked.");
+    }
+    line.put(key, value);
+    if (!defaultKeys.contains(key)) {
+      defaultKeys.add(key);
+    }
+  }
+  
+  public V get(Object key)
+  {
+    if (line == null) {
+      throw new IllegalStateException("line is null: get() method is called before beginLine() is invoked.");
+    }
+    return line.get(key);
+  }
+  
+  public String [] getLines()
+  {
+    return encodeToCsv(null);
+  }
+  
+  public int getRowCount() {
+    return lines.size();
+  }
+  
+  public int getColumnCount() {
+    return defaultKeys.size();
+  }
+  
+  public V get(int row, Object key) {
+    Map<K, V> map = getRow(row);
+    if (map == null) return null;
+    return map.get(key);
+  }
+  
+  public Map<K, V> getRow(int row) {
+    if (row < 0 || getRowCount() <= row) {
+      throw new IllegalArgumentException("Row index out of bounds: " + row);
+    }
+    
+    Map<K, V> map = lines.get(row);
+    return map;
+  }
+
+
+  public String [] encodeToCsv()
+  {
+    return encodeToCsv(null);
+  }
+  
+  public String [] encodeToCsv(Collection<K> keys)
+  {
+    List<String> csvEncodedLines = new ArrayList<String>();
+    
+    if (keys == null) {
+      keys = getDefaultKeys();
+    }
+    
+    String q = getQuoteChar();
+    String s = getSeparatorChar();
+
+    {
+      List<String> items = new ArrayList<String>();
+      for (K key : keys) {
+        String k = codec.encodeKey(key);
+        k = escapeItem(k, q, s);
+        items.add(k);
+      }
+      csvEncodedLines.add(DviUtils.join(s, items));
+    }
+    
+    for (Map<K, V> csvLine : lines) {
+      List<String> items = new ArrayList<String>();
+      for (K key : keys) {
+        V value = csvLine.get(key);
+        String x = codec.encodeValue(key, value);
+        x = escapeItem(x, q, s);
+        items.add(x);
+      }
+      csvEncodedLines.add(DviUtils.join(s, items));
+    }
+    
+    return csvEncodedLines.toArray(new String[csvEncodedLines.size()]);
+  }
+  
+  protected String escapeItem(String x, final String quoteMark, final String separator)
+  {
+    final String q = quoteMark;
+    
+    if (x == null) {
+      x = "";
+    } else if (needEscape(x)) {
+      x = q + x.replaceAll(q, q + q) + q;
+    }
+    return x;
+  }
+  
+  private static final Pattern patNeedEscape = Pattern.compile("[\\r\\n,\"]");
+  protected boolean needEscape(String x) {
+    if (x == null) {
+      return false;
+    } else {
+      Matcher mat = patNeedEscape.matcher(x);
+      return mat.find();
+    }
+  }
+
+  public void writeToFile(File file)
+  throws IOException
+  {
+    LOGGER.fine("Writing CSV data to file: " + file);
+    if (file == null) throw new IllegalArgumentException("file can't be null");
+    FileOutputStream fos = new FileOutputStream(file);
+    try {
+      writeToStream(fos);
+    } finally {
+      fos.flush();
+      DviUtils.silentClose(fos);
+    }
+  }
+  
+  public void writeToStream(OutputStream os)
+  throws IOException
+  {
+    if (os == null) throw new IllegalArgumentException("output stream can't be null");
+    try {
+      PrintWriter pw = new PrintWriter(os);
+      for (String line : getLines()) {
+        pw.println(line);
+      }
+      pw.flush();
+      pw.close();
+    } finally {
+      os.flush();
+      DviUtils.silentClose(os);
+    }
+  }
+  
+  public void readFromFile(File file) throws IOException, CsvException
+  {
+    LOGGER.fine("Reading CSV data from file: " + file);
+    if (file == null) throw new IllegalArgumentException("file can't be null");
+    FileInputStream fis = new FileInputStream(file);
+    try {
+      readFromStream(fis);
+    } finally {
+      DviUtils.silentClose(fis);
+    }
+  }
+  
+  public void readFromStream(InputStream is) throws IOException, CsvException
+  {
+    CsvParser<K, V> parser = new CsvParser<K, V>(codec, this);
+  
+    String [] lines = DviUtils.readLinesFromStream(is);
+    for (String line : lines) {
+      LOGGER.fine("sending line to parser: " + line);
+      parser.feed(line);
+    }
+    parser.close();
+  }
+  
+  private String getSeparatorChar() {
+    return DEFAULT_SEPARATOR;
+  }
+
+  private String getQuoteChar() {
+    return DEFAULT_QUOTE_CHAR;
+  }
+
+  protected Collection<K> getDefaultKeys() {
+    return Collections.unmodifiableList(defaultKeys);
+  }
+
+  public void putAll(Map<K, V> map) {
+    for (Map.Entry<K, V> entry : map.entrySet()) {
+      put(entry.getKey(), entry.getValue());
+    }
+  }
+  
+  public int hashCode()
+  {
+    int hashCode = 0;
+    
+    final int rows = getRowCount();
+    for (int i=0; i<rows; i++) {
+      Map<?, ?> map = getRow(i);
+      hashCode = map.hashCode() + 33 * hashCode;
+    }
+    return hashCode;
+  }
+  
+  public boolean equals(Object o) {
+    if (!(o instanceof CsvData<?, ?>)) {
+      return false;
+    }
+    CsvData<?, ?> cd = (CsvData<?, ?>) o;
+    final int rows = getRowCount();
+    final int cols = getColumnCount();
+    if (cd.getColumnCount() != cols) return false;
+    if (cd.getRowCount() != rows) return false;
+    for (int i=0; i<rows; i++) {
+      Map<?, ?> map1 = cd.getRow(i);
+      Map<?, ?> map2 = getRow(i);
+      if (map1.equals(map2)) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvException.java b/src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvException.java
new file mode 100644 (file)
index 0000000..dd3c622
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.csv;
+public class CsvException extends Exception {
+  private static final long serialVersionUID = 5941058193971803624L;
+
+  public CsvException() {
+  }
+
+  public CsvException(String message) {
+    super(message);
+  }
+
+  public CsvException(Throwable cause) {
+    super(cause);
+  }
+
+  public CsvException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvLineParser.java b/src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvLineParser.java
new file mode 100644 (file)
index 0000000..8cd964f
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.csv;
+import java.util.ArrayList;
+import java.util.List;
+public class CsvLineParser<K, V> {
+  private int state = 0;
+  private StringBuilder sb = null;
+  private List<String> line = null;
+  private List<List<String>> lines = new ArrayList<List<String>>();
+  
+  public String [][] getData()
+  {
+    int cols=0, rows=lines.size();
+    for (List<String> line : lines) {
+      cols = Math.max(cols, line.size());
+    }
+    String [] [] ret = new String[rows][cols];
+    int i=0;
+    for (List<String> line : lines) {
+      int j=0;
+      for (String s : line) {
+        ret[i][j] = s;
+        j++;
+      }
+      i++;
+    }
+    return ret;
+  }
+  
+  public void feed(char c)
+  throws CsvException
+  {
+    if (state == 0 || state == 1) {
+      // Not inside a quotation.
+      if (state == 0) {
+        // Begin of Line
+        openLine();
+      } else {
+        if (sb == null) {
+          openItem();
+        }
+      }
+      if (isQuoteChar(c)) {
+        state = 2;
+      } else if (isSeparator(c)) {
+        closeItem();
+        state = 1;
+      } else if (isNewLine(c)) {
+        closeLine();
+        state = 0;
+      } else {
+        shipout(c);
+        state = 1;
+      }
+    } else if (state == 2) {
+      // Inside a quotation.  Last char is not a quotation.
+      if (isQuoteChar(c)) {
+        state = 3;
+      } else {
+        shipout(c);
+      }
+    } else if (state == 3) {
+      // Inside a quotation.  Last char is a quotation.
+      if (isQuoteChar(c)) {
+        shipout(c);
+        state = 2;
+      } else if (isSeparator(c)) {
+        closeItem();
+        state = 1;
+      } else if (isNewLine(c)) {
+        closeLine();
+        state = 0;
+      } else {
+        throw new CsvException("Illegal character: " + c);
+      }
+    } else if (state == 4) {
+      throw new IllegalStateException("Parser is already closed");
+    } else {
+      throw new IllegalStateException("state=" + state);
+    }
+  }
+  
+  public void close()
+  throws CsvException
+  {
+    if (state == 2) {
+      throw new CsvException("Input ended while parsing quotation.");
+    } else if (state == 3) {
+      closeLine();
+    }
+    state = 4;
+  }
+  
+  protected void openLine() {
+    line = new ArrayList<String>();
+    openItem();
+  }
+
+  protected void closeLine() {
+    closeItem();
+    lines.add(line);
+  }
+  
+  protected void openItem() {
+    sb = new StringBuilder();
+  }
+  
+  protected void closeItem() {
+    line.add(sb.toString());
+    sb = null;
+  }
+
+  private boolean isNewLine(char c) {
+    return c == '\n' || c == '\r';
+  }
+
+  private void shipout(char c) {
+    sb.append(c);
+  }
+  
+  // TODO: outsource the quote char.
+  private boolean isQuoteChar(char c) {
+    return (c == '"');
+  }
+  
+  private boolean isSeparator(char c) {
+    return (c == ',');
+  }
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvParser.java b/src/jp/sourceforge/dvibrowser/dvicore/util/csv/CsvParser.java
new file mode 100644 (file)
index 0000000..1c9ed7d
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.csv;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+public class CsvParser<K, V> {
+  private static final Logger LOGGER = Logger.getLogger(CsvParser.class
+      .getName());
+  private final CsvCellCodec<K, V> codec;
+  private CsvLineParser<K, V> lineParser;
+  private final CsvData<K, V> csvData;
+  
+  public CsvParser(CsvCellCodec<K, V> codec, CsvData<K, V> csvData) {
+    this.codec = codec;
+    this.csvData = csvData;
+    this.lineParser = new CsvLineParser<K, V>();
+  }
+
+  public void feed(String s)
+  throws CsvException
+  {
+    LOGGER.fine("parsing line: " + s);
+    for (char c : s.toCharArray()) {
+      lineParser.feed(c);
+    }
+    lineParser.feed('\n');
+  }
+  
+  public void close() throws CsvException
+  {
+    lineParser.close();
+
+    String [] [] data = lineParser.getData();
+    final int rows = data.length;
+    
+    if (rows > 0) {
+      LOGGER.fine("ship out rows: " + rows);
+      final int cols = data[0].length;
+
+      ArrayList<K> keys = new ArrayList<K>();
+      for (int j = 0; j < data[0].length; j++) {
+        keys.add(codec.decodeKey(data[0][j]));
+      }
+
+      for (int i = 1; i < rows; i++) {
+        csvData.beginLine();
+        for (int j = 0; j < cols; j++) {
+          K key = keys.get(j);
+          V value = codec.decodeValue(key, data[i][j]);
+          csvData.put(key, value);
+          LOGGER.finer("key=" + key + " value=" + value);
+        }
+        csvData.endLine();
+      }
+    } else {
+      LOGGER.fine("no data to ship out");
+    }
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/csv/StringCsvCellCodec.java b/src/jp/sourceforge/dvibrowser/dvicore/util/csv/StringCsvCellCodec.java
new file mode 100644 (file)
index 0000000..bb10f58
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.csv;
+public final class StringCsvCellCodec
+implements CsvCellCodec<String, String>
+{
+  public String encodeKey(String key) {
+    return (key == null) ? "" : key;
+  }
+
+  public String encodeValue(String key, String value) {
+    return (value == null) ? "" : value;
+  }
+
+  public String decodeKey(String s) {
+    return s;
+  }
+
+  public String decodeValue(String key, String s) {
+    return s;
+  }
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/csv/StringCsvData.java b/src/jp/sourceforge/dvibrowser/dvicore/util/csv/StringCsvData.java
new file mode 100644 (file)
index 0000000..c1a2709
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.csv;
+public class StringCsvData
+extends CsvData<String, String>
+{
+  public StringCsvData()
+  {
+    super(new StringCsvCellCodec());
+  }
+  
+  public StringCsvData(CsvCellCodec<String, String> codec)
+  {
+    super(codec);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/progress/AbstractProgressModel.java b/src/jp/sourceforge/dvibrowser/dvicore/util/progress/AbstractProgressModel.java
new file mode 100644 (file)
index 0000000..7ae84e6
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.progress;
+
+import javax.swing.event.EventListenerList;
+
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+
+
+public abstract class AbstractProgressModel extends DviObject {
+
+  protected EventListenerList listenerList = new EventListenerList();
+
+  public AbstractProgressModel(DviContextSupport dcs) {
+    super(dcs);
+  }
+
+  public void addProgressListener(ProgressListener l)
+  {
+      listenerList.add(ProgressListener.class, l);
+  }
+
+  public void removeProgressListener(ProgressListener l)
+  {
+      listenerList.remove(ProgressListener.class, l);
+  }
+
+  protected void fireProgressOpenEvent(ManagedProgressItem item)
+  {
+    ProgressEvent event = null;
+    
+    Object[] listeners = listenerList.getListenerList();
+    for (int i = listeners.length-2; i>=0; i-=2) {
+      if (listeners[i] == ProgressListener.class) {
+        if (event == null)
+          event = new ProgressEvent(this, item);
+        ((ProgressListener)listeners[i+1]).progressOpen(event);
+      }
+    }
+  }
+
+  protected void fireProgressCloseEvent(ManagedProgressItem item)
+  {
+    ProgressEvent event = null;
+    
+    Object[] listeners = listenerList.getListenerList();
+    for (int i = listeners.length-2; i>=0; i-=2) {
+      if (listeners[i] == ProgressListener.class) {
+        if (event == null)
+          event = new ProgressEvent(this, item);
+        ((ProgressListener)listeners[i+1]).progressClose(event);
+      }
+    }
+  }
+
+  protected void fireProgressUpdateEvent(ManagedProgressItem item)
+  {
+    ProgressEvent event = null;
+    
+    Object[] listeners = listenerList.getListenerList();
+    for (int i = listeners.length-2; i>=0; i-=2) {
+      if (listeners[i] == ProgressListener.class) {
+        if (event == null)
+          event = new ProgressEvent(this, item);
+        ((ProgressListener)listeners[i+1]).progressUpdate(event);
+      }
+    }
+  }
+  
+  public abstract ProgressItem getMostRecentItem();
+
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ManagedProgressItem.java b/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ManagedProgressItem.java
new file mode 100644 (file)
index 0000000..7a16710
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.progress;
+
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+
+
+public class ManagedProgressItem
+extends DviObject
+implements ProgressItem
+{
+  private static final Logger LOGGER = Logger.getLogger(ManagedProgressItem.class.getName());
+  
+  public static final int STATE_INIT = 0;
+  public static final int STATE_OPEN = 1;
+  public static final int STATE_CLOSED = 2;
+  private final AbstractProgressModel recorder;
+  private final ProgressItem item;
+  private int start, end, current;
+  private int state = 0;
+  public ManagedProgressItem(AbstractProgressModel recorder, ProgressItem item)
+  {
+    super(recorder);
+    this.recorder = recorder;
+    this.item = item;
+  }
+  
+  public boolean isOpen() {
+    return state == STATE_OPEN;
+  }
+
+  public boolean isClosed() {
+    return state == STATE_CLOSED;
+  }
+  
+  public AbstractProgressModel getProgressRecorder()
+  {
+    return recorder;
+  }
+
+  public ProgressItem getOriginalItem()
+  {
+    return item;
+  }
+
+  public void close() throws DviException
+  {
+    if (state == STATE_INIT)
+      throw new IllegalStateException("Item is never opened.");
+    if (state == STATE_CLOSED) return;
+    state = STATE_CLOSED;
+    this.current = this.end;
+    item.close();
+    LOGGER.fine("close: " + item);
+    recorder.fireProgressCloseEvent(this);
+  }
+
+  public void open(int start, int end) throws DviException
+  {
+    if (state != STATE_INIT)
+      throw new IllegalStateException("Item is not open or closed.");
+    state = STATE_OPEN;
+    this.start = start;
+    this.current = start;
+    this.end = end;
+    item.open(start, end);
+    LOGGER.fine("opened: " + item);
+    recorder.fireProgressOpenEvent(this);
+  }
+
+  public void update(int current) throws DviException
+  {
+    if (state != STATE_OPEN)
+      throw new IllegalStateException("Item is not open or closed.");
+    this.current = current;
+    item.update(current);
+    LOGGER.fine("update: " + item);
+    recorder.fireProgressUpdateEvent(this);
+  }
+  
+  public String toString()
+  {
+    return getClass().getName() + "[" + item.toString() + "]";
+  }
+  
+  protected void finalize()
+  {
+    try {
+      close();
+    } catch (DviException e) {
+      e.printStackTrace();
+    }
+  }
+
+  public int getStart()
+  {
+    return start;
+  }
+
+  public int getEnd()
+  {
+    return end;
+  }
+
+  public int getCurrent()
+  {
+    return current;
+  }
+
+  public int getState()
+  {
+    return state;
+  }
+}
\ No newline at end of file
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressBlock.java b/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressBlock.java
new file mode 100644 (file)
index 0000000..48d17cf
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.progress;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+
+public class ProgressBlock
+extends DviObject
+implements ProgressItem
+{
+  private final String msg;
+  private boolean closed = false;
+  public ProgressBlock(DviContextSupport dcs, String msg)
+  {
+    super(dcs);
+    this.msg = msg;
+  }
+  
+  public void close() throws DviException
+  {
+    closed = true;
+  }
+
+  public void open(int start, int end) throws DviException
+  {
+  }
+
+  public void update(int current) throws DviException
+  {
+  }
+
+  public String getMessage()
+  {
+    return msg;
+  }
+  
+  public String toString()
+  {
+    if (closed) {
+      return "Finished " + msg;
+    } else {
+      return "Now " +  msg;
+    }
+  }
+  
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressEvent.java b/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressEvent.java
new file mode 100644 (file)
index 0000000..0e589cb
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.progress;
+
+import java.awt.Event;
+
+
+public class ProgressEvent extends Event {
+  private static final long serialVersionUID = 1L;
+
+  private final ManagedProgressItem item;
+  
+  public ProgressEvent(Object target, ManagedProgressItem item) {
+    super(target, item.getCurrent(), item);
+    this.item = item;
+  }
+
+  public ProgressItem getItem()
+  {
+    return item.getOriginalItem();
+  }
+
+  public ManagedProgressItem getManagedItem()
+  {
+    return item;
+  }
+
+  public int getStartValue() { return item.getStart(); }
+  public int getEndValue() { return item.getEnd(); }
+  public int getCurrentValue() { return item.getCurrent(); }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressItem.java b/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressItem.java
new file mode 100644 (file)
index 0000000..a95b320
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.progress;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviSerialized;
+
+public interface ProgressItem
+extends DviSerialized
+{
+  public void open(int start, int end) throws DviException;
+  public void update(int current) throws DviException;
+  public void close() throws DviException;
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressListener.java b/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressListener.java
new file mode 100644 (file)
index 0000000..ab4555a
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.progress;
+
+import java.util.EventListener;
+
+public interface ProgressListener
+extends EventListener
+{
+  void progressOpen(ProgressEvent e);
+  void progressUpdate(ProgressEvent e);
+  void progressClose(ProgressEvent e);
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressLogger.java b/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressLogger.java
new file mode 100644 (file)
index 0000000..d74afe8
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.progress;
+
+import java.io.PrintStream;
+
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+
+
+public class ProgressLogger
+extends DviObject implements ProgressListener
+{
+  private final PrintStream out;
+  private ProgressRecorder recorder;
+//  private static ExecutorService exe = Executors.newFixedThreadPool(1, new DaemonThreadFactory());
+
+  public ProgressLogger(DviContextSupport dcs, PrintStream out, ProgressRecorder recorder)
+  {
+    super(dcs);
+    this.out = out;
+    setProgressRecorder(recorder);
+  }
+
+  public void setProgressRecorder(ProgressRecorder recorder)
+  {
+    if (this.recorder != null)
+      this.recorder.removeProgressListener(this);
+    this.recorder = recorder;
+    if (this.recorder != null)
+      this.recorder.addProgressListener(this);
+  }
+
+  public ProgressRecorder getProgressRecorder()
+  {
+    return recorder;
+  }
+
+  public void progressClose(ProgressEvent e)
+  {
+    out.println("[END] " + e.getItem());
+  }
+
+  public void progressOpen(ProgressEvent e)
+  {
+    out.println("[BEGIN] " + e.getItem());
+  }
+
+  public void progressUpdate(ProgressEvent e)
+  {
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressMessage.java b/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressMessage.java
new file mode 100644 (file)
index 0000000..55697dc
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.progress;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+
+public class ProgressMessage
+extends DviObject
+implements ProgressItem
+{
+  private final String msg;
+  public ProgressMessage(DviContextSupport dcs, String msg)
+  {
+    super(dcs);
+    this.msg = msg;
+  }
+  
+  public void close() throws DviException
+  {
+  }
+
+  public void open(int start, int end) throws DviException
+  {
+  }
+
+  public void update(int current) throws DviException
+  {
+  }
+
+  public String getMessage()
+  {
+    return msg;
+  }
+  
+  public String toString()
+  {
+    return msg;
+  }
+  
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressRecorder.java b/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressRecorder.java
new file mode 100644 (file)
index 0000000..735cb7b
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.progress;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+
+
+public class ProgressRecorder
+extends AbstractProgressModel
+{
+  private final ArrayList<ManagedProgressItem> list = new ArrayList<ManagedProgressItem>();
+  public ProgressRecorder(DviContextSupport dcs)
+  {
+    super(dcs);
+  }
+  
+  protected void addInternal(ManagedProgressItem newWrappedItem)
+  {
+    if (newWrappedItem == null) return;
+    synchronized(list) {
+      list.add(newWrappedItem);
+      while (list.size() > 0) {
+        ManagedProgressItem wrappedItem = list.get(0);
+        boolean remove = removeEldestElement(wrappedItem);
+        if (!remove) break;
+        list.remove(0);
+      }
+    }
+  }
+  
+  protected List<ManagedProgressItem> getProgressItems() { return Collections.unmodifiableList(list); }
+  
+  protected boolean removeEldestElement(ManagedProgressItem item)
+  {
+    return false;
+  }
+  
+  public void append(ProgressItem item)
+  throws DviException
+  {
+    if (item == null) return;
+    ManagedProgressItem wrappedItem = (ManagedProgressItem) open(item, 0, 0);
+    wrappedItem.close();
+  }
+  
+  public void append(String msg)
+  throws DviException
+  {
+    append(new ProgressMessage(this, msg));
+  }
+  
+  public ProgressItem open(String msg, int start, int end)
+  throws DviException
+  {
+    ProgressItem item = new ProgressBlock(this, msg);
+    return open(item, start, end);
+  }
+
+  public ProgressItem open(String msg)
+  throws DviException
+  {
+    return open(msg, 0, 1);
+  }
+
+  protected ProgressItem open(ProgressItem item, int start, int end)
+  throws DviException
+  {
+    if (start > end)
+      throw new IllegalArgumentException
+        ("Invalid value range: start=" + start + " end=" + end);
+    ManagedProgressItem wrappedItem = new ManagedProgressItem(this, item);
+    wrappedItem.open(start, end);
+    addInternal(wrappedItem);
+    return wrappedItem;
+  }
+  
+  public ProgressItem getMostRecentItem()
+  {
+    ManagedProgressItem managedItem = null;
+    synchronized(list) {
+      int size = list.size();
+      if (size != 0) 
+        managedItem = list.get(size - 1);
+    }
+    if (managedItem != null) {
+      return managedItem.getOriginalItem();
+    }
+    return null;
+  }
+
+  public ProgressItem [] getOpenItems()
+  {
+    ArrayList<ProgressItem> items = new ArrayList<ProgressItem>();
+    synchronized(list) {
+      for (ManagedProgressItem wrappedItem : list) {
+        if (wrappedItem.isOpen())
+          items.add(wrappedItem.getOriginalItem());
+      }
+    }
+    return items.toArray(new ProgressItem[items.size()]);
+  }
+
+  public ProgressItem [] getItems()
+  {
+    ArrayList<ProgressItem> items = new ArrayList<ProgressItem>();
+    synchronized(list) {
+      for (ManagedProgressItem wrappedItem : list) {
+          items.add(wrappedItem.getOriginalItem());
+      }
+    }
+    return items.toArray(new ProgressItem[items.size()]);
+  }
+}
diff --git a/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressReporter.java b/src/jp/sourceforge/dvibrowser/dvicore/util/progress/ProgressReporter.java
new file mode 100644 (file)
index 0000000..9d07f3f
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * 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.
+ *    
+ * 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.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.util.progress;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+
+
+public class ProgressReporter
+extends AbstractProgressModel
+implements ProgressListener
+{
+  private final List<ProgressRecorder> recorders = new ArrayList<ProgressRecorder>();
+  
+  public ProgressReporter(DviContextSupport dcs)
+  {
+    super(dcs);
+  }
+  
+  public AbstractProgressModel [] getProgressRecorders()
+  {
+    return recorders.toArray(new ProgressRecorder[recorders.size()]);
+  }
+
+  public void addProgressRecorder(ProgressRecorder recorder)
+  {
+    if (recorder == null) return;
+    if (recorders.contains(recorder)) return;
+    recorder.addProgressListener(this);
+    recorders.add(recorder);
+  }
+
+  public void removeProgressRecorder(AbstractProgressModel recorder)
+  {
+    if (recorder == null) return;
+    if (!recorders.contains(recorder)) return;
+    recorders.remove(recorder);
+    recorder.removeProgressListener(this);
+  }
+  
+  private volatile ProgressItem item;
+  public ProgressItem getMostRecentItem()
+  {
+    return item;
+  }
+  
+  protected void update()
+  {
+    ArrayList<ProgressItem> list = new ArrayList<ProgressItem>();
+    for (ProgressRecorder recorder : recorders) {
+      ProgressItem[] items = recorder.getItems();
+      if (items.length != 0) {
+        ProgressItem item = items[items.length - 1];
+        list.add(item);
+      }
+    }
+    Collections.sort(list, new Comparator<ProgressItem>() {
+      public int compare(ProgressItem o1, ProgressItem o2)
+      {
+        long s1 = o1.getSerialNumber();
+        long s2 = o2.getSerialNumber();
+        if (s1 < s2) {
+          return -1;
+        } else if (s1 > s2) {
+          return 1;
+        } else {
+          return 0;
+        }
+      }
+    });
+    if (list.size() > 0) {
+      item = list.get(list.size() - 1);
+    }
+  }
+
+  public void progressClose(ProgressEvent e)
+  {
+    update();
+    fireProgressCloseEvent(e.getManagedItem());
+  }
+
+  public void progressOpen(ProgressEvent e)
+  {
+    update();
+    fireProgressOpenEvent(e.getManagedItem());
+  }
+
+  public void progressUpdate(ProgressEvent e)
+  {
+    update();
+    fireProgressUpdateEvent(e.getManagedItem());
+  }
+}