You've already forked libart-paperback
Hello libart-paperback
This commit is contained in:
17
.editorconfig
Normal file
17
.editorconfig
Normal file
@@ -0,0 +1,17 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
indent_size = 4
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yaml]
|
||||
indent_size = 2
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* text=auto
|
||||
30
.gitea/workflows/on-push.yaml
Normal file
30
.gitea/workflows/on-push.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
name: on-push
|
||||
|
||||
on:
|
||||
push:
|
||||
tags-ignore:
|
||||
- '*'
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: linux
|
||||
container: code.helloryan.se/art/infra/buildenv/x86_64-fedora_42-unified:latest
|
||||
volumes:
|
||||
- /build
|
||||
steps:
|
||||
- name: Configure repository access
|
||||
run: |
|
||||
git config --global http.$GITHUB_SERVER_URL/.extraheader "Authorization: token ${{ secrets.ACT_RUNNER_TOKEN }}"
|
||||
- name: Configure build directory
|
||||
run: |
|
||||
bpkg create -d /build cc config.cxx=clang++ config.cc.coptions="-Wall -Werror -Wno-unknown-pragmas -Wno-overloaded-virtual"
|
||||
- name: Build package
|
||||
run: |
|
||||
cd /build
|
||||
bpkg build --yes --trust-yes $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git##$GITHUB_SHA
|
||||
- name: Test package
|
||||
run: |
|
||||
cd /build
|
||||
bpkg test libart-paperback
|
||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
patreon: helloryan
|
||||
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
.bdep/
|
||||
|
||||
# Local default options files.
|
||||
#
|
||||
.build2/local/
|
||||
|
||||
# Compiler/linker output.
|
||||
#
|
||||
*.d
|
||||
*.t
|
||||
*.i
|
||||
*.i.*
|
||||
*.ii
|
||||
*.ii.*
|
||||
*.o
|
||||
*.obj
|
||||
*.gcm
|
||||
*.pcm
|
||||
*.ifc
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
*.a
|
||||
*.lib
|
||||
*.exp
|
||||
*.pdb
|
||||
*.ilk
|
||||
*.exe
|
||||
*.exe.dlls/
|
||||
*.exe.manifest
|
||||
*.pc
|
||||
|
||||
# Compilation database.
|
||||
#
|
||||
compile_commands.json
|
||||
2878
Doxyfile.in
Normal file
2878
Doxyfile.in
Normal file
File diff suppressed because it is too large
Load Diff
31
LICENSE
Normal file
31
LICENSE
Normal file
@@ -0,0 +1,31 @@
|
||||
Copyright © 2025 Ryan. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. All advertising materials mentioning features or use of this software must
|
||||
display the following acknowledgement:
|
||||
|
||||
This product includes software developed by Ryan, http://helloryan.se/.
|
||||
|
||||
4. Neither the name(s) of the copyright holder(s) nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "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 COPYRIGHT HOLDER 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.
|
||||
49
README.md
Normal file
49
README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
\page readme README
|
||||
|
||||
# libart-paperback
|
||||
|
||||
libart-paperback is a PDF library for C++.
|
||||
|
||||
## Feature Requests
|
||||
|
||||
Feature requests can be sent to ryan@helloryan.se.
|
||||
|
||||
## Build System
|
||||
|
||||
libart-paperback uses the build2 build system for C++. More
|
||||
information about installing build2 is available at https://build2.org.
|
||||
|
||||
This README is primarily intended for developers of libart-paperback.
|
||||
For instructions on getting started with integrating libart-paperback
|
||||
into your project, see \ref getting-started.
|
||||
|
||||
## Project Structure
|
||||
|
||||
The source code for libart-paperback is located at `$src_root$/art/paperback`.
|
||||
Examples are available in `$src_root$/examples`.
|
||||
|
||||
## Documentation
|
||||
|
||||
Doxygen is used to generate the API reference documentation for
|
||||
libart-paperback. If you have Doxygen installed and want to build the
|
||||
documentation, you can do so by executing `b alias{docs}` in the
|
||||
package root.
|
||||
|
||||
The documentation will be written to `$out_root$/docs`.
|
||||
|
||||
The Doxygen configuration is located at `$src_root$/Doxyfile.in`. This
|
||||
file is preprocessed by build2 before Doxygen is invoked.
|
||||
|
||||
## Developer Documentation
|
||||
|
||||
The developer documentation can be built by executing
|
||||
`b alias{docs-private}` in `$src_root$`. This version of the
|
||||
documentation includes classes defined in .cxx files as well as
|
||||
private class members.
|
||||
|
||||
Developer documentation will be written to the same directory as the
|
||||
public documentation, i.e. `$out_root$/docs`.
|
||||
|
||||
## GitHub Pull Requests
|
||||
|
||||
Pull requests on GitHub will definitely be ignored.
|
||||
9
art/paperback/.gitignore
vendored
Normal file
9
art/paperback/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# Generated version header.
|
||||
#
|
||||
version.hxx
|
||||
|
||||
# Unit test executables and Testscript output directories
|
||||
# (can be symlinks).
|
||||
#
|
||||
*.test
|
||||
test-*.test
|
||||
63
art/paperback/buildfile
Normal file
63
art/paperback/buildfile
Normal file
@@ -0,0 +1,63 @@
|
||||
intf_libs = # Interface dependencies.
|
||||
impl_libs = # Implementation dependencies.
|
||||
|
||||
import impl_libs =+ libart-unicode%lib{art-unicode}
|
||||
|
||||
./: lib{art-paperback}: libul{art-paperback}
|
||||
|
||||
libul{art-paperback}: {hxx ixx txx cxx}{** -**.test... -version} \
|
||||
{hxx }{ version}
|
||||
|
||||
libul{art-paperback}: $impl_libs $intf_libs
|
||||
|
||||
test_libs =
|
||||
import test_libs =+ libart-validation%lib{art-validation}
|
||||
|
||||
# Unit tests.
|
||||
#
|
||||
exe{*.test}:
|
||||
{
|
||||
test = true
|
||||
install = false
|
||||
}
|
||||
|
||||
for t: cxx{**.test...}
|
||||
{
|
||||
d = $directory($t)
|
||||
n = $name($t)...
|
||||
|
||||
./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $test_libs $d/testscript{+$n}
|
||||
$d/exe{$n}: libul{art-paperback}: bin.whole = false
|
||||
}
|
||||
|
||||
hxx{version}: in{version} $src_root/manifest
|
||||
|
||||
# Build options.
|
||||
#
|
||||
cxx.poptions =+ "-I$out_root" "-I$src_root"
|
||||
|
||||
# Export options.
|
||||
#
|
||||
lib{art-paperback}:
|
||||
{
|
||||
cxx.export.poptions = "-I$out_root" "-I$src_root"
|
||||
cxx.export.libs = $intf_libs
|
||||
}
|
||||
|
||||
# For pre-releases use the complete version to make sure they cannot
|
||||
# be used in place of another pre-release or the final version. See
|
||||
# the version module for details on the version.* variable values.
|
||||
#
|
||||
if $version.pre_release
|
||||
lib{art-paperback}: bin.lib.version = "-$version.project_id"
|
||||
else
|
||||
lib{art-paperback}: bin.lib.version = "-$version.major.$version.minor"
|
||||
|
||||
# Install into the art/paperback/ subdirectory of, say, /usr/include/
|
||||
# recreating subdirectories.
|
||||
#
|
||||
{hxx ixx txx}{*}:
|
||||
{
|
||||
install = include/art/paperback/
|
||||
install.subdirs = true
|
||||
}
|
||||
203
art/paperback/carousel/array.cxx
Normal file
203
art/paperback/carousel/array.cxx
Normal file
@@ -0,0 +1,203 @@
|
||||
#include <art/paperback/carousel/array.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
Array::
|
||||
Array()
|
||||
{}
|
||||
|
||||
Array::
|
||||
Array(vector<Object> init)
|
||||
: _data{init.begin(), init.end()}
|
||||
{}
|
||||
|
||||
Array::
|
||||
Array(Array const& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Array::
|
||||
Array(Array&& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Array::
|
||||
~Array() noexcept
|
||||
{}
|
||||
|
||||
size_t
|
||||
Array::
|
||||
size() const
|
||||
{
|
||||
return _data.size();
|
||||
}
|
||||
|
||||
bool
|
||||
Array::
|
||||
empty() const
|
||||
{
|
||||
return _data.empty();
|
||||
}
|
||||
|
||||
Object&
|
||||
Array::
|
||||
front()
|
||||
{
|
||||
return _data.front();
|
||||
}
|
||||
|
||||
Object const&
|
||||
Array::
|
||||
front() const
|
||||
{
|
||||
return _data.front();
|
||||
}
|
||||
|
||||
Object&
|
||||
Array::
|
||||
back()
|
||||
{
|
||||
return _data.back();
|
||||
}
|
||||
|
||||
Object const&
|
||||
Array::
|
||||
back() const
|
||||
{
|
||||
return _data.back();
|
||||
}
|
||||
|
||||
Array::iterator
|
||||
Array::
|
||||
begin()
|
||||
{
|
||||
return _data.begin();
|
||||
}
|
||||
|
||||
Array::const_iterator
|
||||
Array::
|
||||
begin() const
|
||||
{
|
||||
return _data.begin();
|
||||
}
|
||||
|
||||
Array::const_iterator
|
||||
Array::
|
||||
cbegin() const
|
||||
{
|
||||
return _data.cbegin();
|
||||
}
|
||||
|
||||
Array::iterator
|
||||
Array::
|
||||
end()
|
||||
{
|
||||
return _data.end();
|
||||
}
|
||||
|
||||
Array::const_iterator
|
||||
Array::
|
||||
end() const
|
||||
{
|
||||
return _data.end();
|
||||
}
|
||||
|
||||
Array::const_iterator
|
||||
Array::
|
||||
cend() const
|
||||
{
|
||||
return _data.cend();
|
||||
}
|
||||
|
||||
Object&
|
||||
Array::
|
||||
push_front(Object const& object)
|
||||
{
|
||||
_data.push_front(object);
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
front().attach(*ptr);
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
|
||||
return _data.front();
|
||||
}
|
||||
|
||||
Object&
|
||||
Array::
|
||||
push_back(Object const& object)
|
||||
{
|
||||
_data.push_back(object);
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
back().attach(*ptr);
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
|
||||
return _data.back();
|
||||
}
|
||||
|
||||
void
|
||||
Array::
|
||||
pop_front()
|
||||
{
|
||||
_data.pop_front();
|
||||
}
|
||||
|
||||
void
|
||||
Array::
|
||||
pop_back()
|
||||
{
|
||||
_data.pop_back();
|
||||
}
|
||||
|
||||
Array&
|
||||
Array::
|
||||
operator=(Array const& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
_data = other._data;
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
attach_children(*ptr);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Array&
|
||||
Array::
|
||||
operator=(Array&& other)
|
||||
{
|
||||
return *this = other;
|
||||
}
|
||||
|
||||
bool
|
||||
Array::
|
||||
operator==(Array const& other) const
|
||||
{
|
||||
return _data == other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Array::
|
||||
operator!=(Array const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void
|
||||
Array::
|
||||
attach_children(Object_model::Owner& owner)
|
||||
{
|
||||
for (auto& j : _data) {
|
||||
j.attach(owner);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
151
art/paperback/carousel/array.hxx
Normal file
151
art/paperback/carousel/array.hxx
Normal file
@@ -0,0 +1,151 @@
|
||||
#ifndef art__paperback__carousel__array_hxx_
|
||||
#define art__paperback__carousel__array_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
class Array
|
||||
: public Object_model::Value_base
|
||||
{
|
||||
public:
|
||||
/// Iterator type.
|
||||
///
|
||||
using iterator = typename deque<Object>::iterator;
|
||||
|
||||
/// Immutbale iterator type.
|
||||
///
|
||||
using const_iterator = typename deque<Object>::const_iterator;
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Array();
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Array(vector<Object>);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Array(Array const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Array(Array&&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Array() noexcept;
|
||||
|
||||
/// Get the size of the array.
|
||||
///
|
||||
size_t
|
||||
size() const;
|
||||
|
||||
/// Check if the array is empty.
|
||||
///
|
||||
bool
|
||||
empty() const;
|
||||
|
||||
/// Get a reference to the first element.
|
||||
///
|
||||
Object&
|
||||
front();
|
||||
|
||||
/// Get a reference to the first element.
|
||||
///
|
||||
Object const&
|
||||
front() const;
|
||||
|
||||
/// Get a reference to the last element.
|
||||
///
|
||||
Object&
|
||||
back();
|
||||
|
||||
/// Get a reference to the last element.
|
||||
///
|
||||
Object const&
|
||||
back() const;
|
||||
|
||||
/// Get begin iterator.
|
||||
///
|
||||
iterator
|
||||
begin();
|
||||
|
||||
/// Get begin iterator.
|
||||
///
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
/// Get begin iterator.
|
||||
///
|
||||
const_iterator
|
||||
cbegin() const;
|
||||
|
||||
/// Get past-the-end iterator.
|
||||
///
|
||||
iterator
|
||||
end();
|
||||
|
||||
/// Get past-the-end iterator.
|
||||
///
|
||||
const_iterator
|
||||
end() const;
|
||||
|
||||
/// Get past-the-end iterator.
|
||||
///
|
||||
const_iterator
|
||||
cend() const;
|
||||
|
||||
/// Push element to the front of the array.
|
||||
///
|
||||
Object&
|
||||
push_front(Object const&);
|
||||
|
||||
/// Push element to the back of the array.
|
||||
///
|
||||
Object&
|
||||
push_back(Object const&);
|
||||
|
||||
void
|
||||
pop_front();
|
||||
|
||||
void
|
||||
pop_back();
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Array&
|
||||
operator=(Array const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Array&
|
||||
operator=(Array&&);
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator==(Array const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator!=(Array const&) const;
|
||||
|
||||
protected:
|
||||
void
|
||||
attach_children(Object_model::Owner&) override;
|
||||
|
||||
private:
|
||||
deque<Object> _data;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
72
art/paperback/carousel/boolean.cxx
Normal file
72
art/paperback/carousel/boolean.cxx
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <art/paperback/carousel/boolean.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
Boolean::
|
||||
Boolean()
|
||||
{}
|
||||
|
||||
Boolean::
|
||||
Boolean(bool value)
|
||||
: _data{value}
|
||||
{}
|
||||
|
||||
Boolean::
|
||||
Boolean(Boolean const& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Boolean::
|
||||
Boolean(Boolean&& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Boolean::
|
||||
~Boolean() noexcept
|
||||
{}
|
||||
|
||||
bool const&
|
||||
Boolean::
|
||||
operator*() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
Boolean&
|
||||
Boolean::
|
||||
operator=(Boolean const& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
_data = other._data;
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Boolean&
|
||||
Boolean::
|
||||
operator=(Boolean&& other)
|
||||
{
|
||||
return *this = other;
|
||||
}
|
||||
|
||||
bool
|
||||
Boolean::
|
||||
operator==(Boolean const& other) const
|
||||
{
|
||||
return _data == other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Boolean::
|
||||
operator!=(Boolean const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
70
art/paperback/carousel/boolean.hxx
Normal file
70
art/paperback/carousel/boolean.hxx
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef art__paperback__carousel__boolean_hxx_
|
||||
#define art__paperback__carousel__boolean_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Represents a COS boolean.
|
||||
///
|
||||
class Boolean
|
||||
: public Object_model::Value_base
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
Boolean();
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Boolean(bool);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Boolean(Boolean const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Boolean(Boolean&&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Boolean() noexcept;
|
||||
|
||||
/// Access boolean.
|
||||
///
|
||||
bool const&
|
||||
operator*() const;
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Boolean&
|
||||
operator=(Boolean const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Boolean&
|
||||
operator=(Boolean&&);
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator==(Boolean const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator!=(Boolean const&) const;
|
||||
|
||||
private:
|
||||
bool _data{};
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
300
art/paperback/carousel/cross-reference.cxx
Normal file
300
art/paperback/carousel/cross-reference.cxx
Normal file
@@ -0,0 +1,300 @@
|
||||
#include <art/paperback/carousel/cross-reference.hxx>
|
||||
|
||||
#include <art/paperback/except.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Represents a free entry in the cross-reference table.
|
||||
///
|
||||
/// In contrast with used entries, free entries are always written
|
||||
/// to the cross-reference section of the COS-file.
|
||||
///
|
||||
struct cross_Reference::Free_entry
|
||||
{
|
||||
/// The next free index in the cross-reference table.
|
||||
///
|
||||
Index next{};
|
||||
|
||||
/// The generation of the next entry.
|
||||
///
|
||||
Generation next_generation{};
|
||||
|
||||
};
|
||||
|
||||
/// Represents a used entry in the cross-reference table.
|
||||
///
|
||||
/// The offset of the object is set during write, and only entries
|
||||
/// with a set offset will be written to the COS-file
|
||||
/// cross-reference section.
|
||||
///
|
||||
struct cross_Reference::Used_entry
|
||||
{
|
||||
/// The offset in the COS-file of this entry.
|
||||
///
|
||||
optional<int64_t> offset{};
|
||||
|
||||
};
|
||||
|
||||
/// \class cross_Reference
|
||||
///
|
||||
/// The cross-reference table maintains a link between an object
|
||||
/// index/generation and its position in the COS-file.
|
||||
///
|
||||
/// The paperback implementation of the PDF 1.4 specification
|
||||
/// does not re-use the index of a previously deleted object, hence
|
||||
/// the generation of new objects will always be zero (0).
|
||||
///
|
||||
|
||||
/// This constructor creates a new cross-reference table.
|
||||
///
|
||||
/// Object 0 will be automatically allocated.
|
||||
///
|
||||
cross_Reference::
|
||||
cross_Reference()
|
||||
{
|
||||
// Make sure to allocate the special object 0.
|
||||
//
|
||||
_table[Identity{0, 0}] = Free_entry{0, 0xffff};
|
||||
}
|
||||
|
||||
cross_Reference::
|
||||
cross_Reference(cross_Reference const&) = default;
|
||||
|
||||
cross_Reference::
|
||||
cross_Reference(cross_Reference&&) = default;
|
||||
|
||||
cross_Reference::
|
||||
~cross_Reference() noexcept
|
||||
{}
|
||||
|
||||
/// \return Returns the size of the cross-reference table.
|
||||
///
|
||||
uint32_t
|
||||
cross_Reference::
|
||||
size() const
|
||||
{
|
||||
uint32_t size{};
|
||||
|
||||
for (auto const& j : _table) {
|
||||
if (j.first.index > size) {
|
||||
size = j.first.index;
|
||||
}
|
||||
}
|
||||
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
/// \return Returns the identity of the newly allocated object.
|
||||
///
|
||||
Identity
|
||||
cross_Reference::
|
||||
allocate()
|
||||
{
|
||||
auto identity = next();
|
||||
_table[identity] = Used_entry{nullopt};
|
||||
return identity;
|
||||
}
|
||||
|
||||
/// \param identity The object identity for which to retrieve the offset.
|
||||
/// \return Returns the file offset of the object.
|
||||
///
|
||||
int64_t
|
||||
cross_Reference::
|
||||
get_offset(Identity identity) const
|
||||
{
|
||||
#if 0
|
||||
auto it = _table.find(identity);
|
||||
|
||||
if (it == _table.end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (it->second.free) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return it->second.offset;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \throw Internal_error Thrown if the object is not allocated.
|
||||
///
|
||||
void
|
||||
cross_Reference::
|
||||
set_offset(Identity identity, int64_t offset)
|
||||
{
|
||||
auto entry = _table.find(identity);
|
||||
|
||||
if (entry == _table.end()) {
|
||||
raise<Internal_error>{} << "invalid object index";
|
||||
}
|
||||
|
||||
if (!holds_alternative<Used_entry>(entry->second)) {
|
||||
raise<Internal_error>{} << "invalid object index";
|
||||
}
|
||||
|
||||
std::get<Used_entry>(entry->second).offset = offset;
|
||||
}
|
||||
|
||||
/// \return Returns the index of the next available object.
|
||||
///
|
||||
Identity
|
||||
cross_Reference::
|
||||
next() const
|
||||
{
|
||||
Index next{};
|
||||
|
||||
for (auto const& j : _table) {
|
||||
if (next < j.first.index) {
|
||||
next = j.first.index;
|
||||
}
|
||||
}
|
||||
|
||||
return Identity{next + 1, 0};
|
||||
}
|
||||
|
||||
/// \param w The COS-file writer.
|
||||
/// \param update True if this is an update; false otherwise.
|
||||
///
|
||||
void
|
||||
cross_Reference::
|
||||
write(Writer& w, bool update)
|
||||
{
|
||||
w.begin_xref();
|
||||
|
||||
// Cross-reference table entry.
|
||||
//
|
||||
struct Entry
|
||||
{
|
||||
// The index of this entry.
|
||||
//
|
||||
Index index{};
|
||||
|
||||
// Either the offset for used entries, or the next free index
|
||||
// for free entries.
|
||||
//
|
||||
uint32_t param0{};
|
||||
|
||||
// Either the generation for used entries, or the next
|
||||
// generation for free entries.
|
||||
//
|
||||
uint16_t param1{};
|
||||
|
||||
// Is this a free entry?
|
||||
//
|
||||
bool free{};
|
||||
|
||||
};
|
||||
|
||||
vector<Entry> entries;
|
||||
|
||||
for (auto it = _table.begin(); it != _table.end(); ++it) {
|
||||
auto v = [&](auto&& entry)
|
||||
{
|
||||
using T = std::decay_t<decltype(entry)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, Used_entry>) {
|
||||
// Ignore used entries without an offset. This will be the
|
||||
// case for allocated objects that hasn't been written out.
|
||||
//
|
||||
if (entry.offset == nullopt) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto index = it->first.index;
|
||||
auto offset = *entry.offset;
|
||||
auto generation = it->first.generation;
|
||||
|
||||
entries.emplace_back(
|
||||
index,
|
||||
offset,
|
||||
generation,
|
||||
false
|
||||
);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, Free_entry>) {
|
||||
// As we do not support the removal of objects, we only
|
||||
// write free entries when writing the first cross-reference
|
||||
// table in a file.
|
||||
//
|
||||
if (update) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto index = it->first.index;
|
||||
auto next = entry.next;
|
||||
auto next_generation = entry.next_generation;
|
||||
|
||||
entries.emplace_back(
|
||||
index,
|
||||
next,
|
||||
next_generation,
|
||||
true
|
||||
);
|
||||
}
|
||||
else {
|
||||
static_assert(false, "non-exhaustive visitor");
|
||||
}
|
||||
};
|
||||
|
||||
std::visit(v, it->second);
|
||||
}
|
||||
|
||||
// Write the cross-reference table.
|
||||
//
|
||||
for (auto it = entries.begin(); it != entries.end();) {
|
||||
uint32_t start{it->index};
|
||||
uint32_t count{1};
|
||||
|
||||
auto k = it;
|
||||
auto prev = k++;
|
||||
|
||||
while (
|
||||
k != entries.end() && k->index == prev->index + 1
|
||||
) {
|
||||
prev = k++;
|
||||
++count;
|
||||
}
|
||||
|
||||
w.begin_xref_chunk(start, count);
|
||||
|
||||
auto kt = it;
|
||||
|
||||
for (uint32_t i{0}; i < count; ++i, ++kt) {
|
||||
if (kt->free) {
|
||||
w.write_xref_free_entry(kt->param0, kt->param1);
|
||||
continue;
|
||||
}
|
||||
|
||||
w.write_xref_used_entry(kt->param0, kt->param1);
|
||||
}
|
||||
|
||||
it = k;
|
||||
}
|
||||
}
|
||||
|
||||
// This is typically called after the cross-reference table has been
|
||||
// written out.
|
||||
//
|
||||
void
|
||||
cross_Reference::
|
||||
clear_offsets()
|
||||
{
|
||||
for (auto& j : _table) {
|
||||
if (holds_alternative<Used_entry>(j.second)) {
|
||||
std::get<Used_entry>(j.second).offset = nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cross_Reference&
|
||||
cross_Reference::
|
||||
operator=(cross_Reference const&) = default;
|
||||
|
||||
cross_Reference&
|
||||
cross_Reference::
|
||||
operator=(cross_Reference&&) = default;
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
92
art/paperback/carousel/cross-reference.hxx
Normal file
92
art/paperback/carousel/cross-reference.hxx
Normal file
@@ -0,0 +1,92 @@
|
||||
#ifndef art__paperback__carousel__cross_reference_hxx_
|
||||
#define art__paperback__carousel__cross_reference_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/writer.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Implements the COS cross-reference table.
|
||||
///
|
||||
class cross_Reference
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
cross_Reference();
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
cross_Reference(cross_Reference const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
cross_Reference(cross_Reference&&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~cross_Reference() noexcept;
|
||||
|
||||
/// Get the size of the cross-reference table.
|
||||
///
|
||||
uint32_t
|
||||
size() const;
|
||||
|
||||
/// Allocate a new object index.
|
||||
///
|
||||
Identity
|
||||
allocate();
|
||||
|
||||
/// Get the offset of a used entry.
|
||||
///
|
||||
int64_t
|
||||
get_offset(Identity) const;
|
||||
|
||||
/// Set the offset of a used entry.
|
||||
///
|
||||
void
|
||||
set_offset(Identity, int64_t);
|
||||
|
||||
/// Get the next available object identity.
|
||||
///
|
||||
Identity
|
||||
next() const;
|
||||
|
||||
/// Write the cross-reference to an output stream.
|
||||
///
|
||||
void
|
||||
write(Writer&, bool);
|
||||
|
||||
/// Clears the offsets of used entries.
|
||||
///
|
||||
void
|
||||
clear_offsets();
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
cross_Reference&
|
||||
operator=(cross_Reference const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
cross_Reference&
|
||||
operator=(cross_Reference&&);
|
||||
|
||||
private:
|
||||
struct Free_entry;
|
||||
struct Used_entry;
|
||||
|
||||
using Entry = variant<Free_entry, Used_entry>;
|
||||
|
||||
/// Cross-reference table entries.
|
||||
///
|
||||
map<Identity, Entry> _table;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
183
art/paperback/carousel/dictionary.cxx
Normal file
183
art/paperback/carousel/dictionary.cxx
Normal file
@@ -0,0 +1,183 @@
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
#include <art/paperback/carousel/undefined.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
Dictionary::
|
||||
Dictionary()
|
||||
{}
|
||||
|
||||
Dictionary::
|
||||
Dictionary(map<Name, Object> data)
|
||||
: _data{std::move(data)}
|
||||
{}
|
||||
|
||||
Dictionary::
|
||||
Dictionary(Dictionary const& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Dictionary::
|
||||
Dictionary(Dictionary&& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Dictionary::
|
||||
~Dictionary() noexcept
|
||||
{}
|
||||
|
||||
size_t
|
||||
Dictionary::
|
||||
size() const
|
||||
{
|
||||
return _data.size();
|
||||
}
|
||||
|
||||
bool
|
||||
Dictionary::
|
||||
empty() const
|
||||
{
|
||||
return _data.empty();
|
||||
}
|
||||
|
||||
bool
|
||||
Dictionary::
|
||||
contains(Name const& key)
|
||||
{
|
||||
return _data.contains(key);
|
||||
}
|
||||
|
||||
Object&
|
||||
Dictionary::
|
||||
at(Name const& key)
|
||||
{
|
||||
return _data.at(key);
|
||||
}
|
||||
|
||||
Object const&
|
||||
Dictionary::
|
||||
at(Name const& key) const
|
||||
{
|
||||
return _data.at(key);
|
||||
}
|
||||
|
||||
Dictionary::iterator
|
||||
Dictionary::
|
||||
begin()
|
||||
{
|
||||
return _data.begin();
|
||||
}
|
||||
|
||||
Dictionary::const_iterator
|
||||
Dictionary::
|
||||
begin() const
|
||||
{
|
||||
return _data.begin();
|
||||
}
|
||||
|
||||
Dictionary::const_iterator
|
||||
Dictionary::
|
||||
cbegin() const
|
||||
{
|
||||
return _data.cbegin();
|
||||
}
|
||||
|
||||
Dictionary::iterator
|
||||
Dictionary::
|
||||
end()
|
||||
{
|
||||
return _data.end();
|
||||
}
|
||||
|
||||
Dictionary::const_iterator
|
||||
Dictionary::
|
||||
end() const
|
||||
{
|
||||
return _data.end();
|
||||
}
|
||||
|
||||
Dictionary::const_iterator
|
||||
Dictionary::
|
||||
cend() const
|
||||
{
|
||||
return _data.cend();
|
||||
}
|
||||
|
||||
void
|
||||
Dictionary::
|
||||
insert(Name const& key, Object object)
|
||||
{
|
||||
if (is_of_type<Undefined>(object)) {
|
||||
erase(key);
|
||||
return;
|
||||
}
|
||||
|
||||
auto [it, _] = _data.insert_or_assign(key, object);
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
it->second.attach(*ptr);
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Dictionary::
|
||||
erase(Name const& key)
|
||||
{
|
||||
_data.erase(key);
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary&
|
||||
Dictionary::
|
||||
operator=(Dictionary const& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
_data = other._data;
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
attach_children(*ptr);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dictionary&
|
||||
Dictionary::
|
||||
operator=(Dictionary&& other)
|
||||
{
|
||||
return operator=(other);
|
||||
}
|
||||
|
||||
bool
|
||||
Dictionary::
|
||||
operator==(Dictionary const& other) const
|
||||
{
|
||||
return _data == other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Dictionary::
|
||||
operator!=(Dictionary const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void
|
||||
Dictionary::
|
||||
attach_children(Object_model::Owner& owner)
|
||||
{
|
||||
for (auto& j : _data) {
|
||||
j.second.attach(owner);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
144
art/paperback/carousel/dictionary.hxx
Normal file
144
art/paperback/carousel/dictionary.hxx
Normal file
@@ -0,0 +1,144 @@
|
||||
#ifndef art__paperback__carousel__dictionary_hxx_
|
||||
#define art__paperback__carousel__dictionary_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Represents a COS dictionary.
|
||||
///
|
||||
class Dictionary
|
||||
: public Object_model::Value_base
|
||||
{
|
||||
public:
|
||||
/// Iterator type.
|
||||
///
|
||||
using iterator = typename map<Name, Object>::iterator;
|
||||
|
||||
/// Immutable iterator type.
|
||||
///
|
||||
using const_iterator = typename map<Name, Object>::const_iterator;
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Dictionary();
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
explicit
|
||||
Dictionary(map<Name, Object>);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Dictionary(Dictionary const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Dictionary(Dictionary&&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Dictionary() noexcept;
|
||||
|
||||
/// Get the size of the dictionary.
|
||||
///
|
||||
size_t
|
||||
size() const;
|
||||
|
||||
/// Check if the dictionary is empty.
|
||||
////
|
||||
bool
|
||||
empty() const;
|
||||
|
||||
/// Check if the dictionary contains a key.
|
||||
///
|
||||
bool
|
||||
contains(Name const&);
|
||||
|
||||
/// Access entry.
|
||||
///
|
||||
Object&
|
||||
at(Name const&);
|
||||
|
||||
/// Access entry.
|
||||
///
|
||||
Object const&
|
||||
at(Name const&) const;
|
||||
|
||||
/// Get begin iterator.
|
||||
///
|
||||
iterator
|
||||
begin();
|
||||
|
||||
/// Get begin iterator.
|
||||
///
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
/// Get begin iterator.
|
||||
///
|
||||
const_iterator
|
||||
cbegin() const;
|
||||
|
||||
/// Get past-the-end iterator.
|
||||
///
|
||||
iterator
|
||||
end();
|
||||
|
||||
/// Get past-the-end iterator.
|
||||
///
|
||||
const_iterator
|
||||
end() const;
|
||||
|
||||
/// Get past-the-end iterator.
|
||||
///
|
||||
const_iterator
|
||||
cend() const;
|
||||
|
||||
/// Insert entry into dictionary.
|
||||
///
|
||||
void
|
||||
insert(Name const&, Object);
|
||||
|
||||
/// Remove entry from dictionary.
|
||||
///
|
||||
void
|
||||
erase(Name const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Dictionary&
|
||||
operator=(Dictionary const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Dictionary&
|
||||
operator=(Dictionary&&);
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator==(Dictionary const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator!=(Dictionary const&) const;
|
||||
|
||||
protected:
|
||||
void
|
||||
attach_children(Object_model::Owner&) override;
|
||||
|
||||
private:
|
||||
map<Name, Object> _data;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
305
art/paperback/carousel/file.cxx
Normal file
305
art/paperback/carousel/file.cxx
Normal file
@@ -0,0 +1,305 @@
|
||||
#include <art/paperback/carousel/file.hxx>
|
||||
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
#include <art/paperback/carousel/integer.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Represents a COS-file revision.
|
||||
///
|
||||
struct File::Revision
|
||||
{
|
||||
/// Construct a new revision.
|
||||
///
|
||||
Revision(int64_t offset, cross_Reference xref)
|
||||
: offset{offset},
|
||||
xref{std::move(xref)}
|
||||
{}
|
||||
|
||||
/// The offset of this revision's cross-reference table.
|
||||
///
|
||||
int64_t offset{};
|
||||
|
||||
/// The cross-reference table for this revision.
|
||||
///
|
||||
cross_Reference xref;
|
||||
|
||||
};
|
||||
|
||||
/// COS-file internals.
|
||||
///
|
||||
struct File::Internal
|
||||
{
|
||||
/// Constructor.
|
||||
///
|
||||
/// This constructor is intended to be used when creating a new
|
||||
/// COS-file.
|
||||
///
|
||||
Internal(Create_new const&,
|
||||
iostream& ios,
|
||||
string header,
|
||||
int major,
|
||||
int minor)
|
||||
: ios{ios},
|
||||
header{std::move(header)},
|
||||
major{major},
|
||||
minor{minor}
|
||||
{}
|
||||
|
||||
/// Underlying io stream.
|
||||
///
|
||||
iostream& ios;
|
||||
|
||||
/// The file header.
|
||||
///
|
||||
string header;
|
||||
|
||||
/// The major version.
|
||||
///
|
||||
int major{};
|
||||
|
||||
/// The minor version.
|
||||
///
|
||||
int minor{};
|
||||
|
||||
/// Previous revisions of this file.
|
||||
///
|
||||
/// Revisions are created when writing the file, or when opening
|
||||
/// an existing file.
|
||||
///
|
||||
deque<Revision> revisions;
|
||||
|
||||
/// All objects in this file. Populated as objects are created or
|
||||
/// during file opening.
|
||||
///
|
||||
map<Identity, Object_model::Owner> objects;
|
||||
|
||||
/// The current revision's cross-reference table.
|
||||
///
|
||||
cross_Reference xref;
|
||||
|
||||
/// The document catalog, see Section 3.6.1, “Document Catalog” in
|
||||
/// the PDF-1.4 specification. Allocated on first use or when
|
||||
/// opening an existing PDF-file.
|
||||
///
|
||||
optional<Object> document_catalog;
|
||||
|
||||
/// The document information dictionary, see Section 9.2.1,
|
||||
/// “Document Information Dictionary” in the PDF-1.4
|
||||
/// specification. Allocated on first use or when opening an
|
||||
/// existing PDF-file.
|
||||
///
|
||||
optional<Object> document_information;
|
||||
|
||||
};
|
||||
|
||||
/// \class File
|
||||
///
|
||||
/// Object generations are not used for new files in the
|
||||
/// paperback implementation of the COS file format.
|
||||
///
|
||||
/// Newly allocated objects are always assigned the next available
|
||||
/// object index with a generation of 0.
|
||||
///
|
||||
/// Note though that paperback io support _enerations when
|
||||
/// opening an existing PDF file. That is, the index and generation
|
||||
/// will be used when looking up an object reference.
|
||||
///
|
||||
|
||||
/// Creates a new COS-file.
|
||||
///
|
||||
/// The parameters \a major and \a minor correspond to the version
|
||||
/// of the PDF-document which the file represents.
|
||||
///
|
||||
/// \param ios The io stream for this COS-file.
|
||||
/// \param major The major version of the document.
|
||||
/// \param minor The minor version of the document.
|
||||
/// \param header The COS-file header, e.g. \c PDF-1.4.
|
||||
///
|
||||
File::
|
||||
File(Create_new const&,
|
||||
iostream& ios,
|
||||
int major,
|
||||
int minor,
|
||||
string header)
|
||||
: internal{
|
||||
new Internal{create_new, ios, std::move(header), major, minor}
|
||||
}
|
||||
{}
|
||||
|
||||
/// This destructor will automatically commit the current revision,
|
||||
/// but any exceptions thrown during write will be ignored.
|
||||
///
|
||||
File::
|
||||
~File() noexcept
|
||||
{
|
||||
try {
|
||||
commit_revision();
|
||||
}
|
||||
catch (...) {
|
||||
// Nothing to do here, so just ignore the exception.
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
/// \return Returns a reference to the COS-file catalog dictionary.
|
||||
///
|
||||
Dictionary&
|
||||
File::
|
||||
catalog()
|
||||
{
|
||||
if (!internal->document_catalog) {
|
||||
internal->document_catalog.emplace(create_object<Dictionary>());
|
||||
}
|
||||
|
||||
return object_cast<Dictionary>(*internal->document_catalog);
|
||||
}
|
||||
|
||||
/// \return Returns a reference to the COS-file info dictionary.
|
||||
///
|
||||
Dictionary&
|
||||
File::
|
||||
info()
|
||||
{
|
||||
if (!internal->document_information) {
|
||||
internal->document_information.emplace(create_object<Dictionary>());
|
||||
}
|
||||
|
||||
return object_cast<Dictionary>(*internal->document_information);
|
||||
}
|
||||
|
||||
/// Calling this function will write all objects in the current
|
||||
/// revision to the output stream.
|
||||
///
|
||||
/// A new revision will be created automatically.
|
||||
///
|
||||
/// All modified objects will be marked as clean.
|
||||
///
|
||||
void
|
||||
File::
|
||||
commit_revision()
|
||||
{
|
||||
// Is this an update or a new file?
|
||||
//
|
||||
bool const update = internal->revisions.size() > 0;
|
||||
|
||||
Writer w{internal->ios, internal->major, internal->minor};
|
||||
|
||||
if (!update) {
|
||||
// Write out header at the start for new files.
|
||||
//
|
||||
internal->ios.seekp(0);
|
||||
w.write_header(internal->header);
|
||||
}
|
||||
else {
|
||||
// Seek to the end for updates.
|
||||
//
|
||||
internal->ios.seekp(0, std::ios_base::end);
|
||||
}
|
||||
|
||||
bool objects_written{false};
|
||||
|
||||
for (auto& j : internal->objects) {
|
||||
if (!j.second.modified()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
objects_written = true;
|
||||
|
||||
auto offset = static_cast<int64_t>(internal->ios.tellp());
|
||||
auto identity = j.first;
|
||||
|
||||
internal->xref.set_offset(identity, offset);
|
||||
|
||||
w.write_object(identity, j.second.container());
|
||||
|
||||
// Clear the modifed mark.
|
||||
//
|
||||
j.second.reset();
|
||||
}
|
||||
|
||||
// Skip writing the rest if this is an update and no objects have
|
||||
// been written out.
|
||||
//
|
||||
if (update && !objects_written) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto xref_offset = internal->ios.tellp();
|
||||
internal->xref.write(w, update);
|
||||
|
||||
Dictionary trailer;
|
||||
|
||||
// Size of the cross-reference table. Required.
|
||||
//
|
||||
trailer.insert("Size", Integer{internal->xref.size()});
|
||||
|
||||
if (internal->document_catalog) {
|
||||
trailer.insert("Root", *internal->document_catalog);
|
||||
}
|
||||
|
||||
if (internal->document_information) {
|
||||
trailer.insert("Info", *internal->document_information);
|
||||
}
|
||||
|
||||
// Point to the previous revision, if we have one.
|
||||
//
|
||||
if (internal->revisions.size() > 0) {
|
||||
auto const& prev = internal->revisions.back();
|
||||
trailer.insert("Prev", Integer{prev.offset});
|
||||
}
|
||||
|
||||
w.write_trailer(trailer);
|
||||
w.write_eof(xref_offset);
|
||||
|
||||
// Add the revision we just wrote.
|
||||
//
|
||||
internal->revisions.emplace_back(xref_offset, internal->xref);
|
||||
|
||||
// Reset the offsets of the used objects in the cross-reference
|
||||
// table. This ensures only modified objects will be written to
|
||||
// the cross-reference section during the next commit.
|
||||
//
|
||||
internal->xref.clear_offsets();
|
||||
|
||||
// Make sure everything is flushed.
|
||||
//
|
||||
internal->ios.flush();
|
||||
}
|
||||
|
||||
Object_model::Container_base&
|
||||
File::
|
||||
get_container(Identity const& identity)
|
||||
{
|
||||
return internal->objects.at(identity).container();
|
||||
}
|
||||
|
||||
Object_model::Container_base const&
|
||||
File::
|
||||
get_container(Identity const& identity) const
|
||||
{
|
||||
return internal->objects.at(identity).container();
|
||||
}
|
||||
|
||||
/// \return Returns the allocated index.
|
||||
///
|
||||
Identity
|
||||
File::
|
||||
allocate()
|
||||
{
|
||||
return internal->xref.allocate();
|
||||
}
|
||||
|
||||
/// \param identity The identity of the object.
|
||||
/// \param container The container of the object.
|
||||
///
|
||||
void
|
||||
File::
|
||||
insert(Identity const& identity,
|
||||
shared_ptr<Object_model::Container_base> container)
|
||||
{
|
||||
internal->objects.emplace(identity, std::move(container));
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
107
art/paperback/carousel/file.hxx
Normal file
107
art/paperback/carousel/file.hxx
Normal file
@@ -0,0 +1,107 @@
|
||||
#ifndef art__paperback__carousel__file_hxx_
|
||||
#define art__paperback__carousel__file_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/array.hxx>
|
||||
#include <art/paperback/carousel/cross-reference.hxx>
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
#include <art/paperback/carousel/writer.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Represents a COS-format file.
|
||||
///
|
||||
class File
|
||||
{
|
||||
public:
|
||||
struct Create_new {};
|
||||
|
||||
/// Dispatch-tag for creating a new COS-file.
|
||||
///
|
||||
static constexpr Create_new create_new{};
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
File(Create_new const&, iostream&, int, int, string);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~File() noexcept;
|
||||
|
||||
/// Access the COS-file catalog dictionary object.
|
||||
///
|
||||
Dictionary&
|
||||
catalog();
|
||||
|
||||
/// Access the COS-file info dictionary object.
|
||||
///
|
||||
Dictionary&
|
||||
info();
|
||||
|
||||
/// Create a new object.
|
||||
///
|
||||
template<typename T, typename... Args>
|
||||
Object
|
||||
create_object(Args&&... args)
|
||||
{
|
||||
auto identity = allocate();
|
||||
|
||||
auto container = make_shared<Object_model::Container<T>>(
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
|
||||
insert(identity, std::move(container));
|
||||
|
||||
return Object{
|
||||
make_shared<Object_model::Reference>(*this, identity)
|
||||
};
|
||||
}
|
||||
|
||||
/// Commit the current COS-file revision to the output stream.
|
||||
///
|
||||
void
|
||||
commit_revision();
|
||||
|
||||
private:
|
||||
friend Object_model::Reference;
|
||||
|
||||
/// Get container.
|
||||
///
|
||||
Object_model::Container_base&
|
||||
get_container(Identity const&);
|
||||
|
||||
/// Get container.
|
||||
///
|
||||
Object_model::Container_base const&
|
||||
get_container(Identity const&) const;
|
||||
|
||||
private:
|
||||
File(File const&) = delete;
|
||||
File(File&&) = delete;
|
||||
File& operator=(File const&) = delete;
|
||||
File& operator=(File&&) = delete;
|
||||
|
||||
/// Allocates a new index.
|
||||
///
|
||||
Identity
|
||||
allocate();
|
||||
|
||||
/// Inserts an object into the cross-reference table.
|
||||
///
|
||||
void
|
||||
insert(Identity const&, shared_ptr<Object_model::Container_base>);
|
||||
|
||||
struct Revision;
|
||||
|
||||
struct Internal;
|
||||
unique_ptr<Internal> internal;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
72
art/paperback/carousel/integer.cxx
Normal file
72
art/paperback/carousel/integer.cxx
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <art/paperback/carousel/integer.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
Integer::
|
||||
Integer(int64_t value)
|
||||
: _data{value}
|
||||
{}
|
||||
|
||||
Integer::
|
||||
Integer(Integer const& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Integer::
|
||||
Integer(Integer&& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Integer::
|
||||
~Integer() noexcept
|
||||
{}
|
||||
|
||||
int64_t const&
|
||||
Integer::
|
||||
operator*() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
Integer&
|
||||
Integer::
|
||||
operator=(Integer const& other)
|
||||
{
|
||||
_data = other._data;
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Integer&
|
||||
Integer::
|
||||
operator=(Integer&& other)
|
||||
{
|
||||
_data = other._data;
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
Integer::
|
||||
operator==(Integer const& other) const
|
||||
{
|
||||
return _data == other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Integer::
|
||||
operator!=(Integer const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
66
art/paperback/carousel/integer.hxx
Normal file
66
art/paperback/carousel/integer.hxx
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef art__paperback__carousel__integer_hxx_
|
||||
#define art__paperback__carousel__integer_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Represents a COS integer.
|
||||
///
|
||||
class Integer
|
||||
: public Object_model::Value_base
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
Integer(int64_t);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Integer(Integer const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Integer(Integer&&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Integer() noexcept;
|
||||
|
||||
/// Access integer.
|
||||
///
|
||||
int64_t const&
|
||||
operator*() const;
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Integer&
|
||||
operator=(Integer const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Integer&
|
||||
operator=(Integer&&);
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator==(Integer const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator!=(Integer const&) const;
|
||||
|
||||
private:
|
||||
int64_t _data{};
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
112
art/paperback/carousel/name.cxx
Normal file
112
art/paperback/carousel/name.cxx
Normal file
@@ -0,0 +1,112 @@
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
Name::
|
||||
Name()
|
||||
{}
|
||||
|
||||
Name::
|
||||
Name(string data)
|
||||
: _data{std::move(data)}
|
||||
{}
|
||||
|
||||
Name::
|
||||
Name(char const* data)
|
||||
: _data{data}
|
||||
{}
|
||||
|
||||
Name::
|
||||
Name(Name const& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Name::
|
||||
Name(Name&& other)
|
||||
: _data{std::move(other._data)}
|
||||
{}
|
||||
|
||||
Name::
|
||||
~Name() noexcept
|
||||
{}
|
||||
|
||||
string const&
|
||||
Name::
|
||||
operator*() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
string const*
|
||||
Name::
|
||||
operator->() const
|
||||
{
|
||||
return &_data;
|
||||
}
|
||||
|
||||
Name&
|
||||
Name::
|
||||
operator=(Name const& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
_data = other._data;
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Name&
|
||||
Name::
|
||||
operator=(Name&& other)
|
||||
{
|
||||
return *this = other;
|
||||
}
|
||||
|
||||
bool
|
||||
Name::
|
||||
operator==(Name const& other) const
|
||||
{
|
||||
return _data == other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Name::
|
||||
operator!=(Name const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool
|
||||
Name::
|
||||
operator<(Name const& other) const
|
||||
{
|
||||
return _data < other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Name::
|
||||
operator<=(Name const& other) const
|
||||
{
|
||||
return _data <= other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Name::
|
||||
operator>(Name const& other) const
|
||||
{
|
||||
return _data > other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Name::
|
||||
operator>=(Name const& other) const
|
||||
{
|
||||
return _data >= other._data;
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
99
art/paperback/carousel/name.hxx
Normal file
99
art/paperback/carousel/name.hxx
Normal file
@@ -0,0 +1,99 @@
|
||||
#ifndef art__paperback__carousel__name_hxx_
|
||||
#define art__paperback__carousel__name_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Represents a COS name.
|
||||
///
|
||||
class Name
|
||||
: public Object_model::Value_base
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
Name();
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Name(string);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Name(char const*);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Name(Name const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Name(Name&&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Name() noexcept;
|
||||
|
||||
/// Access string value.
|
||||
///
|
||||
string const&
|
||||
operator*() const;
|
||||
|
||||
/// Access string value.
|
||||
///
|
||||
string const*
|
||||
operator->() const;
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Name&
|
||||
operator=(Name const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Name&
|
||||
operator=(Name&&);
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator==(Name const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator!=(Name const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator<(Name const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator<=(Name const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator>(Name const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator>=(Name const&) const;
|
||||
|
||||
private:
|
||||
string _data;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
214
art/paperback/carousel/object-model.cxx
Normal file
214
art/paperback/carousel/object-model.cxx
Normal file
@@ -0,0 +1,214 @@
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
#include <art/paperback/carousel/file.hxx>
|
||||
|
||||
#include <art/paperback/except.hxx>
|
||||
|
||||
|
||||
namespace Art::Paperback::Carousel::Object_model
|
||||
{
|
||||
|
||||
/// \class Abstract
|
||||
///
|
||||
|
||||
Abstract::
|
||||
~Abstract() noexcept
|
||||
{}
|
||||
|
||||
Abstract::
|
||||
Abstract()
|
||||
{}
|
||||
|
||||
/// \class Container_base
|
||||
///
|
||||
|
||||
Container_base::
|
||||
~Container_base() noexcept
|
||||
{}
|
||||
|
||||
Container_base::
|
||||
Container_base()
|
||||
{}
|
||||
|
||||
/// \class Reference
|
||||
///
|
||||
///
|
||||
|
||||
/// \param file The parent file.
|
||||
/// \param identity The identity of the reference.
|
||||
///
|
||||
Reference::
|
||||
Reference(File& file, Identity identity)
|
||||
: _file{file},
|
||||
_identity{identity}
|
||||
{}
|
||||
|
||||
File&
|
||||
Reference::
|
||||
file() const
|
||||
{
|
||||
return _file;
|
||||
}
|
||||
|
||||
Identity const&
|
||||
Reference::
|
||||
identity() const
|
||||
{
|
||||
return _identity;
|
||||
}
|
||||
|
||||
std::type_info const&
|
||||
Reference::
|
||||
type() const
|
||||
{
|
||||
return typeid(Reference);
|
||||
}
|
||||
|
||||
Container_base&
|
||||
Reference::
|
||||
container()
|
||||
{
|
||||
return file().get_container(identity());
|
||||
}
|
||||
|
||||
Container_base const&
|
||||
Reference::
|
||||
container() const
|
||||
{
|
||||
return file().get_container(identity());
|
||||
}
|
||||
|
||||
Owner*
|
||||
Reference::
|
||||
owner()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Owner const*
|
||||
Reference::
|
||||
owner() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Reference::
|
||||
attach(Owner& owner)
|
||||
{
|
||||
// No-op.
|
||||
//
|
||||
}
|
||||
|
||||
shared_ptr<Abstract>
|
||||
Reference::
|
||||
clone() const
|
||||
{
|
||||
return make_shared<Reference>(file(), identity());
|
||||
}
|
||||
|
||||
/// \class Value_base
|
||||
///
|
||||
|
||||
/// \param owner Reference to the owner.
|
||||
///
|
||||
void
|
||||
Value_base::
|
||||
attach(Owner& owner)
|
||||
{
|
||||
if (_owner) {
|
||||
raise<Internal_error>{} << "object already attached";
|
||||
}
|
||||
|
||||
_owner = &owner;
|
||||
attach_children(owner);
|
||||
}
|
||||
|
||||
/// \return Returns a pointer to the owner, if attached.
|
||||
///
|
||||
Owner*
|
||||
Value_base::
|
||||
owner()
|
||||
{
|
||||
return _owner;
|
||||
}
|
||||
|
||||
/// \return Returns a pointer to the owner, if attached.
|
||||
///
|
||||
Owner const*
|
||||
Value_base::
|
||||
owner() const
|
||||
{
|
||||
return _owner;
|
||||
}
|
||||
|
||||
Value_base::
|
||||
Value_base()
|
||||
{}
|
||||
|
||||
Value_base::
|
||||
~Value_base()
|
||||
{}
|
||||
|
||||
/// \param owner The owner of the children.
|
||||
///
|
||||
void
|
||||
Value_base::
|
||||
attach_children(Owner& owner)
|
||||
{}
|
||||
|
||||
/// \class Owner
|
||||
///
|
||||
|
||||
/// \param container The object container.
|
||||
///
|
||||
Owner::
|
||||
Owner(shared_ptr<Container_base> container)
|
||||
: _container{container}
|
||||
{
|
||||
if (!_container) {
|
||||
raise<Internal_error>{} << "container must not be null";
|
||||
}
|
||||
|
||||
_container->attach(*this);
|
||||
}
|
||||
|
||||
Owner::
|
||||
~Owner() noexcept
|
||||
{}
|
||||
|
||||
Container_base&
|
||||
Owner::
|
||||
container()
|
||||
{
|
||||
return *_container;
|
||||
}
|
||||
|
||||
Container_base const&
|
||||
Owner::
|
||||
container() const
|
||||
{
|
||||
return *_container;
|
||||
}
|
||||
|
||||
bool
|
||||
Owner::
|
||||
modified() const
|
||||
{
|
||||
return _modified;
|
||||
}
|
||||
|
||||
void
|
||||
Owner::
|
||||
mark_as_modified()
|
||||
{
|
||||
_modified = true;
|
||||
}
|
||||
|
||||
void
|
||||
Owner::
|
||||
reset()
|
||||
{
|
||||
_modified = false;
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel::Object_model
|
||||
394
art/paperback/carousel/object-model.hxx
Normal file
394
art/paperback/carousel/object-model.hxx
Normal file
@@ -0,0 +1,394 @@
|
||||
#ifndef art__paperback__carousel__object_model_hxx_
|
||||
#define art__paperback__carousel__object_model_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
#include <art/paperback/visitor.hxx>
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
namespace Art::Paperback::Carousel::Object_model
|
||||
{
|
||||
|
||||
/// Abstract base class for object tree.
|
||||
///
|
||||
class Abstract
|
||||
{
|
||||
public:
|
||||
/// Destructor.
|
||||
///
|
||||
virtual
|
||||
~Abstract() noexcept;
|
||||
|
||||
/// Get contained type info.
|
||||
///
|
||||
virtual
|
||||
std::type_info const&
|
||||
type() const = 0;
|
||||
|
||||
/// Get container.
|
||||
///
|
||||
virtual
|
||||
Container_base&
|
||||
container() = 0;
|
||||
|
||||
/// Get container.
|
||||
///
|
||||
virtual
|
||||
Container_base const&
|
||||
container() const = 0;
|
||||
|
||||
/// Access node owner.
|
||||
///
|
||||
virtual
|
||||
Owner*
|
||||
owner() = 0;
|
||||
|
||||
/// Access node owner.
|
||||
///
|
||||
virtual
|
||||
Owner const*
|
||||
owner() const = 0;
|
||||
|
||||
/// Attach node to owner.
|
||||
///
|
||||
virtual
|
||||
void
|
||||
attach(Owner&) = 0;
|
||||
|
||||
/// Clone.
|
||||
///
|
||||
virtual
|
||||
shared_ptr<Abstract>
|
||||
clone() const = 0;
|
||||
|
||||
protected:
|
||||
/// Constructor.
|
||||
///
|
||||
Abstract();
|
||||
|
||||
private:
|
||||
Abstract(Abstract const&) = delete;
|
||||
Abstract(Abstract&&) = delete;
|
||||
Abstract& operator=(Abstract const&) = delete;
|
||||
Abstract& operator=(Abstract&&) = delete;
|
||||
|
||||
};
|
||||
|
||||
/// Base class for containers.
|
||||
///
|
||||
class Container_base
|
||||
: public Abstract
|
||||
{
|
||||
public:
|
||||
/// Destructor.
|
||||
///
|
||||
~Container_base() noexcept override;
|
||||
|
||||
/// Get.
|
||||
///
|
||||
template<typename T>
|
||||
T&
|
||||
get()
|
||||
{
|
||||
return dynamic_cast<Container<T>&>(*this).get();
|
||||
}
|
||||
|
||||
/// Get.
|
||||
///
|
||||
template<typename T>
|
||||
T const&
|
||||
get() const
|
||||
{
|
||||
return dynamic_cast<Container<T> const&>(*this).get();
|
||||
}
|
||||
|
||||
virtual
|
||||
void
|
||||
assign(Container_base const&) = 0;
|
||||
|
||||
virtual
|
||||
bool
|
||||
compare(Container_base const&) const = 0;
|
||||
|
||||
virtual
|
||||
void
|
||||
do_accept(Visitor&) = 0;
|
||||
|
||||
virtual
|
||||
void
|
||||
do_accept(Visitor&) const = 0;
|
||||
|
||||
protected:
|
||||
/// Constructor.
|
||||
///
|
||||
Container_base();
|
||||
|
||||
private:
|
||||
Container_base(Container_base const&) = delete;
|
||||
Container_base(Container_base&&) = delete;
|
||||
Container_base& operator=(Container_base const&) = delete;
|
||||
Container_base& operator=(Container_base&&) = delete;
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Container
|
||||
: public Container_base,
|
||||
public std::enable_shared_from_this<Container<T>>
|
||||
{
|
||||
public:
|
||||
template<typename... Args>
|
||||
explicit
|
||||
Container(Args&&... args)
|
||||
: _value{std::forward<Args>(args)...}
|
||||
{}
|
||||
|
||||
Container_base&
|
||||
container() override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
Container_base const&
|
||||
container() const override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::type_info const&
|
||||
type() const override
|
||||
{
|
||||
return typeid(T);
|
||||
}
|
||||
|
||||
T&
|
||||
get()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
T const&
|
||||
get() const
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
void
|
||||
assign(Container_base const& other) override
|
||||
{
|
||||
_value = other.get<T>();
|
||||
}
|
||||
|
||||
bool
|
||||
compare(Container_base const& other) const override
|
||||
{
|
||||
return _value == other.get<T>();
|
||||
}
|
||||
|
||||
void
|
||||
do_accept(Visitor& v) override
|
||||
{
|
||||
accept(_value, v);
|
||||
}
|
||||
|
||||
void
|
||||
do_accept(Visitor& v) const override
|
||||
{
|
||||
accept(_value, v);
|
||||
}
|
||||
|
||||
Owner*
|
||||
owner() override
|
||||
{
|
||||
return _value.owner();
|
||||
}
|
||||
|
||||
Owner const*
|
||||
owner() const override
|
||||
{
|
||||
return _value.owner();
|
||||
}
|
||||
|
||||
void
|
||||
attach(Owner& owner) override
|
||||
{
|
||||
return _value.attach(owner);
|
||||
}
|
||||
|
||||
shared_ptr<Abstract>
|
||||
clone() const override
|
||||
{
|
||||
return make_shared<Container<T>>(_value);
|
||||
}
|
||||
|
||||
private:
|
||||
Container(Container<T> const&) = delete;
|
||||
Container(Container<T>&&) = delete;
|
||||
Container<T>& operator=(Container<T> const&) = delete;
|
||||
Container<T>& operator=(Container<T>&&) = delete;
|
||||
|
||||
private:
|
||||
T _value;
|
||||
|
||||
};
|
||||
|
||||
/// Represents a reference to an indirect object.
|
||||
///
|
||||
class Reference
|
||||
: public Abstract,
|
||||
public std::enable_shared_from_this<Reference>
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
Reference(File&, Identity);
|
||||
|
||||
/// Access parent file.
|
||||
///
|
||||
File&
|
||||
file() const;
|
||||
|
||||
/// Access the identity of this reference.
|
||||
///
|
||||
Identity const&
|
||||
identity() const;
|
||||
|
||||
std::type_info const&
|
||||
type() const override;
|
||||
|
||||
Container_base&
|
||||
container() override;
|
||||
|
||||
Container_base const&
|
||||
container() const override;
|
||||
|
||||
Owner*
|
||||
owner() override;
|
||||
|
||||
Owner const*
|
||||
owner() const override;
|
||||
|
||||
void
|
||||
attach(Owner& owner) override;
|
||||
|
||||
/// Clone.
|
||||
///
|
||||
shared_ptr<Abstract>
|
||||
clone() const override;
|
||||
|
||||
private:
|
||||
Reference(Reference const&) = delete;
|
||||
Reference(Reference&&) = delete;
|
||||
Reference& operator=(Reference const&) = delete;
|
||||
Reference& operator=(Reference&&) = delete;
|
||||
|
||||
private:
|
||||
File& _file;
|
||||
Identity _identity;
|
||||
|
||||
};
|
||||
|
||||
/// Base class for value types.
|
||||
///
|
||||
class Value_base
|
||||
{
|
||||
public:
|
||||
/// Attach value to owner.
|
||||
///
|
||||
void
|
||||
attach(Owner&);
|
||||
|
||||
/// Access owner, if any.
|
||||
///
|
||||
Owner*
|
||||
owner();
|
||||
|
||||
/// Access owner, if any.
|
||||
///
|
||||
Owner const*
|
||||
owner() const;
|
||||
|
||||
protected:
|
||||
/// Constructor.
|
||||
///
|
||||
Value_base();
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Value_base();
|
||||
|
||||
/// Attach any children.
|
||||
///
|
||||
virtual
|
||||
void
|
||||
attach_children(Owner&);
|
||||
|
||||
private:
|
||||
Value_base(Value_base const&) = delete;
|
||||
Value_base(Value_base&&) = delete;
|
||||
Value_base& operator=(Value_base const&) = delete;
|
||||
Value_base& operator=(Value_base&&) = delete;
|
||||
|
||||
private:
|
||||
Owner* _owner{};
|
||||
|
||||
};
|
||||
|
||||
/// Owner of an indirect object.
|
||||
///
|
||||
class Owner
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
explicit
|
||||
Owner(shared_ptr<Container_base>);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Owner() noexcept;
|
||||
|
||||
/// Access container.
|
||||
///
|
||||
Container_base&
|
||||
container();
|
||||
|
||||
/// Access container.
|
||||
///
|
||||
Container_base const&
|
||||
container() const;
|
||||
|
||||
/// Check if modified.
|
||||
///
|
||||
bool
|
||||
modified() const;
|
||||
|
||||
/// Mark as modified.
|
||||
///
|
||||
void
|
||||
mark_as_modified();
|
||||
|
||||
/// Reset modification mark.
|
||||
///
|
||||
void
|
||||
reset();
|
||||
|
||||
private:
|
||||
Owner(Owner const&) = delete;
|
||||
Owner(Owner&&) = delete;
|
||||
Owner& operator=(Owner const&) = delete;
|
||||
Owner& operator=(Owner&&) = delete;
|
||||
|
||||
private:
|
||||
shared_ptr<Container_base> _container;
|
||||
|
||||
/// Change tracking.
|
||||
///
|
||||
bool _modified{true};
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel::Object_model
|
||||
|
||||
#endif
|
||||
326
art/paperback/carousel/object.cxx
Normal file
326
art/paperback/carousel/object.cxx
Normal file
@@ -0,0 +1,326 @@
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
|
||||
#include <art/paperback/except.hxx>
|
||||
|
||||
#include <art/paperback/carousel/file.hxx>
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
#include <art/paperback/carousel/array.hxx>
|
||||
#include <art/paperback/carousel/boolean.hxx>
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
#include <art/paperback/carousel/integer.hxx>
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
#include <art/paperback/carousel/real.hxx>
|
||||
#include <art/paperback/carousel/stream.hxx>
|
||||
#include <art/paperback/carousel/text.hxx>
|
||||
#include <art/paperback/carousel/undefined.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// \class Object
|
||||
///
|
||||
/// The Object class represents a COS-file object. An object can be
|
||||
/// either a direct object or an indirect object. The eight supported
|
||||
/// object types are:
|
||||
///
|
||||
/// - Boolean values
|
||||
/// - Integer and real numbers
|
||||
/// - Strings
|
||||
/// - Names
|
||||
/// - Arrays
|
||||
/// - Dictionaries
|
||||
/// - Streams
|
||||
/// - Null (undefined)
|
||||
///
|
||||
/// See Section 3.2, "Objects" in the PDF-1.4 specification for
|
||||
/// further information.
|
||||
///
|
||||
/// Instances of Object maintain a pointer to the underlying data.
|
||||
/// Copying an object creates a new reference to the same underlying
|
||||
/// data. This is not to be confused with indirect COS objects. See
|
||||
/// the example below for clarification. Also note, that an object
|
||||
/// can only have a maximum of one owner, so adding an object to
|
||||
/// say two different arrays at the same time will result in an
|
||||
/// exception being thrown.
|
||||
///
|
||||
/// \todo Describe assigning to already created objects.
|
||||
///
|
||||
/// ```cxx
|
||||
/// using namespace Art::Paperback::Carousel;
|
||||
///
|
||||
/// // Create a direct object of array type.
|
||||
/// //
|
||||
/// Object object0{Array{}};
|
||||
///
|
||||
/// // Create a new direct object pointing to the same data as object0.
|
||||
/// //
|
||||
/// Object object1{object0};
|
||||
///
|
||||
/// // Changes made in object0 will be reflected in object1, and vice versa.
|
||||
/// //
|
||||
///
|
||||
/// // If an identitcal copy is desired, the clone function can be used:
|
||||
/// //
|
||||
/// Object object2{clone(object0)};
|
||||
///
|
||||
/// // Changes made to object2 will not be reflected in object0 nor object1.
|
||||
/// //
|
||||
///
|
||||
/// // Indirect objects work differently. Cloning an indirect object
|
||||
/// // will clone the reference to the indirect object, not the data
|
||||
/// // of the object.
|
||||
/// //
|
||||
///
|
||||
/// File file;
|
||||
///
|
||||
/// // Create an indirect object of array type.
|
||||
/// //
|
||||
/// Object object3{file.create_object<Array>()};
|
||||
///
|
||||
/// Object object4{object3};
|
||||
/// Object object5{clone(object3)};
|
||||
///
|
||||
/// // At this point, both object4 and object5 point to the indirect
|
||||
/// // object first allocated in object3. This includes object5 since
|
||||
/// // it's a clone of a reference.
|
||||
/// //
|
||||
/// ```
|
||||
///
|
||||
/// Access to the data of an object is provided by the \ref object_cast
|
||||
/// function, see the example below:
|
||||
///
|
||||
/// ```cxx
|
||||
/// using namespace Art::Paperback::Carousel;
|
||||
///
|
||||
/// Object my_array{Array{}};
|
||||
///
|
||||
/// object_cast<Array>(my_array).push_back(Integer{0});
|
||||
/// ```
|
||||
///
|
||||
|
||||
/// This constructor creates a null (undefined) object.
|
||||
///
|
||||
Object::
|
||||
Object(Undefined const& undefined)
|
||||
: _data{make_shared<Object_model::Container<Undefined>>(undefined)}
|
||||
{}
|
||||
|
||||
/// This constructor creates an array object.
|
||||
///
|
||||
/// \param array The array.
|
||||
///
|
||||
Object::
|
||||
Object(Array array)
|
||||
: _data{make_shared<Object_model::Container<Array>>(std::move(array))}
|
||||
{}
|
||||
|
||||
/// This constructor creates a boolean object.
|
||||
///
|
||||
/// \param boolean The boolean value.
|
||||
///
|
||||
Object::
|
||||
Object(Boolean boolean)
|
||||
: _data{make_shared<Object_model::Container<Boolean>>(std::move(boolean))}
|
||||
{}
|
||||
|
||||
/// This constructor creates a dictionary object.
|
||||
///
|
||||
/// \param dictionary The dictionary.
|
||||
///
|
||||
Object::
|
||||
Object(Dictionary dictionary)
|
||||
: _data{make_shared<Object_model::Container<Dictionary>>(std::move(dictionary))}
|
||||
{}
|
||||
|
||||
/// This constructor creates an integer object.
|
||||
///
|
||||
/// \param integer The integer value.
|
||||
///
|
||||
Object::
|
||||
Object(Integer integer)
|
||||
: _data{make_shared<Object_model::Container<Integer>>(std::move(integer))}
|
||||
{}
|
||||
|
||||
/// This constructor creates a name object.
|
||||
///
|
||||
/// \param name The name.
|
||||
///
|
||||
Object::
|
||||
Object(Name name)
|
||||
: _data{make_shared<Object_model::Container<Name>>(std::move(name))}
|
||||
{}
|
||||
|
||||
/// This constructor creates a real object.
|
||||
///
|
||||
/// \param real The real value.
|
||||
///
|
||||
Object::
|
||||
Object(Real real)
|
||||
: _data{make_shared<Object_model::Container<Real>>(std::move(real))}
|
||||
{}
|
||||
|
||||
/// This constructor creates a text (string) object.
|
||||
///
|
||||
/// \param text The text (string) value.
|
||||
///
|
||||
Object::
|
||||
Object(Text text)
|
||||
: _data{make_shared<Object_model::Container<Text>>(std::move(text))}
|
||||
{}
|
||||
|
||||
/// \param other The object to copy from.
|
||||
///
|
||||
Object::
|
||||
Object(Object const& other)
|
||||
: _data{other._data}
|
||||
{
|
||||
if (!_data) {
|
||||
raise<Internal_error>{} << "invalid object data";
|
||||
}
|
||||
}
|
||||
|
||||
/// This constructor makes a copy of \a other rather than moving
|
||||
/// from it. This avoids the problem of having invalid-state
|
||||
/// objects.
|
||||
///
|
||||
/// \param other The object to move from.
|
||||
///
|
||||
Object::
|
||||
Object(Object&& other)
|
||||
: _data{other._data}
|
||||
{
|
||||
if (!_data) {
|
||||
raise<Internal_error>{} << "invalid object data";
|
||||
}
|
||||
}
|
||||
|
||||
/// \param owner The owner to tie this object with.
|
||||
///
|
||||
void
|
||||
Object::
|
||||
attach(Object_model::Owner& owner)
|
||||
{
|
||||
_data->attach(owner);
|
||||
}
|
||||
|
||||
/// \return Returns true if this object is an indirect reference.
|
||||
///
|
||||
bool
|
||||
Object::
|
||||
is_reference() const
|
||||
{
|
||||
return typeid(Object_model::Reference) == _data->type();
|
||||
}
|
||||
|
||||
/// \throw Internal_error Thrown if the object is not an indirect object.
|
||||
/// \return Returns the identity of the object, if it is an indirect
|
||||
/// object.
|
||||
///
|
||||
Identity const&
|
||||
Object::
|
||||
identity() const
|
||||
{
|
||||
if (is_reference()) {
|
||||
return dynamic_cast<Object_model::Reference const&>(*_data).identity();
|
||||
}
|
||||
|
||||
raise<Internal_error>{} << "object is not a reference";
|
||||
}
|
||||
|
||||
/// \param other The object to assign from.
|
||||
///
|
||||
Object&
|
||||
Object::
|
||||
operator=(Object const& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
container().assign(other.container());
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \param other The object to assign from.
|
||||
///
|
||||
Object&
|
||||
Object::
|
||||
operator=(Object&& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
container().assign(other.container());
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \param other The object to compare with.
|
||||
///
|
||||
bool
|
||||
Object::
|
||||
operator==(Object const& other) const
|
||||
{
|
||||
return container().compare(other.container());
|
||||
}
|
||||
|
||||
/// \param other The object to compare with.
|
||||
///
|
||||
bool
|
||||
Object::
|
||||
operator!=(Object const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
/// This is an internal constructor to create an indirect object.
|
||||
///
|
||||
Object::
|
||||
Object(shared_ptr<Object_model::Abstract> data)
|
||||
: _data{std::move(data)}
|
||||
{
|
||||
if (!_data) {
|
||||
raise<Internal_error>{} << "data must not be null";
|
||||
}
|
||||
}
|
||||
|
||||
Object_model::Container_base&
|
||||
Object::
|
||||
container()
|
||||
{
|
||||
return _data->container();
|
||||
}
|
||||
|
||||
Object_model::Container_base const&
|
||||
Object::
|
||||
container() const
|
||||
{
|
||||
return _data->container();
|
||||
}
|
||||
|
||||
/// \param object The object to clone.
|
||||
///
|
||||
Object
|
||||
clone(Object& object)
|
||||
{
|
||||
return Object{object._data->clone()};
|
||||
}
|
||||
|
||||
/// \param visitee The visitee.
|
||||
/// \param v The visitor.
|
||||
///
|
||||
void
|
||||
accept(Object& visitee, Visitor& v)
|
||||
{
|
||||
visitee.container().do_accept(v);
|
||||
}
|
||||
|
||||
/// \param visitee The visitee.
|
||||
/// \param v The visitor.
|
||||
///
|
||||
void
|
||||
accept(Object const& visitee, Visitor& v)
|
||||
{
|
||||
visitee.container().do_accept(v);
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
192
art/paperback/carousel/object.hxx
Normal file
192
art/paperback/carousel/object.hxx
Normal file
@@ -0,0 +1,192 @@
|
||||
#ifndef art__paperback__carousel__object_hxx_
|
||||
#define art__paperback__carousel__object_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
#include <art/paperback/visitor.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Represents a COS-file object.
|
||||
///
|
||||
class Object
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
Object(Undefined const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Object(Array);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Object(Boolean);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Object(Dictionary);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Object(Integer);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Object(Name);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Object(Real);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Object(Text);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Object(Object const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Object(Object&&);
|
||||
|
||||
/// Attach object to owner.
|
||||
///
|
||||
void
|
||||
attach(Object_model::Owner&);
|
||||
|
||||
/// Check if the objcet is a reference.
|
||||
///
|
||||
bool
|
||||
is_reference() const;
|
||||
|
||||
/// Get object identity, if reference.
|
||||
///
|
||||
Identity const&
|
||||
identity() const;
|
||||
|
||||
template<typename>
|
||||
friend
|
||||
bool
|
||||
is_of_type(Object const&);
|
||||
|
||||
template<typename T>
|
||||
friend
|
||||
T&
|
||||
object_cast(Object&);
|
||||
|
||||
template<typename T>
|
||||
friend
|
||||
T const&
|
||||
object_cast(Object const&);
|
||||
|
||||
friend
|
||||
Object
|
||||
clone(Object&);
|
||||
|
||||
friend
|
||||
void
|
||||
accept(Object&, Visitor&);
|
||||
|
||||
friend
|
||||
void
|
||||
accept(Object const&, Visitor&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Object&
|
||||
operator=(Object const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Object&
|
||||
operator=(Object&&);
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator==(Object const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator!=(Object const&) const;
|
||||
|
||||
private:
|
||||
friend File;
|
||||
friend Writer;
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
explicit
|
||||
Object(shared_ptr<Object_model::Abstract>);
|
||||
|
||||
/// Get the container of the object.
|
||||
///
|
||||
Object_model::Container_base&
|
||||
container();
|
||||
|
||||
/// Get the container of the object.
|
||||
///
|
||||
Object_model::Container_base const&
|
||||
container() const;
|
||||
|
||||
private:
|
||||
/// Holds the data of the object, either an instance of
|
||||
/// Object_model::Reference for references, or an instance of
|
||||
/// Object_model::Container for direct objects.
|
||||
///
|
||||
shared_ptr<Object_model::Abstract> _data;
|
||||
|
||||
};
|
||||
|
||||
/// Check object type.
|
||||
///
|
||||
template<typename T>
|
||||
bool
|
||||
is_of_type(Object const& object)
|
||||
{
|
||||
return typeid(T) == object.container().type();
|
||||
}
|
||||
|
||||
/// This function provides access to the value of an object.
|
||||
///
|
||||
template<typename T>
|
||||
T&
|
||||
object_cast(Object& object)
|
||||
{
|
||||
return object.container().get<T>();
|
||||
}
|
||||
|
||||
/// This function provides access to the value of an object.
|
||||
///
|
||||
template<typename T>
|
||||
T const&
|
||||
object_cast(Object const& object)
|
||||
{
|
||||
return object.container().get<T>();
|
||||
}
|
||||
|
||||
/// Clone an object.
|
||||
///
|
||||
Object
|
||||
clone(Object&);
|
||||
|
||||
/// Accept visitor on object.
|
||||
///
|
||||
void
|
||||
accept(Object&, Visitor&);
|
||||
|
||||
/// Accept visitor on object.
|
||||
///
|
||||
void
|
||||
accept(Object const&, Visitor&);
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
75
art/paperback/carousel/object.test.cxx
Normal file
75
art/paperback/carousel/object.test.cxx
Normal file
@@ -0,0 +1,75 @@
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
#include <art/paperback/carousel/text.hxx>
|
||||
|
||||
#include <art/validation/main.hxx>
|
||||
|
||||
using namespace Art::Paperback;
|
||||
using namespace Art::Paperback::Carousel;
|
||||
|
||||
// Test the identity of two objects.
|
||||
//
|
||||
VALIDATION_TEST(identity)
|
||||
{
|
||||
Object object0{Text{"initial"}};
|
||||
|
||||
VALIDATION_ASSERT_EQUAL(
|
||||
*object_cast<Text>(object0),
|
||||
"initial"
|
||||
);
|
||||
|
||||
Object object1{object0};
|
||||
|
||||
VALIDATION_ASSERT_EQUAL(
|
||||
object_cast<Text>(object0),
|
||||
object_cast<Text>(object1)
|
||||
);
|
||||
|
||||
object0 = Text{"changed"};
|
||||
|
||||
VALIDATION_ASSERT_EQUAL(
|
||||
*object_cast<Text>(object0),
|
||||
"changed"
|
||||
);
|
||||
|
||||
VALIDATION_ASSERT_EQUAL(
|
||||
object_cast<Text>(object0),
|
||||
object_cast<Text>(object1)
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure changing a cloned object does not change the source object.
|
||||
//
|
||||
VALIDATION_TEST(cloning)
|
||||
{
|
||||
Object object0{Text{"initial"}};
|
||||
|
||||
VALIDATION_ASSERT_EQUAL(
|
||||
*object_cast<Text>(object0),
|
||||
"initial"
|
||||
);
|
||||
|
||||
Object object1 = clone(object0);
|
||||
|
||||
VALIDATION_ASSERT_EQUAL(
|
||||
object_cast<Text>(object0),
|
||||
object_cast<Text>(object1)
|
||||
);
|
||||
|
||||
object0 = Text{"changed"};
|
||||
|
||||
VALIDATION_ASSERT_EQUAL(
|
||||
*object_cast<Text>(object0),
|
||||
"changed"
|
||||
);
|
||||
|
||||
VALIDATION_ASSERT_NOT_EQUAL(
|
||||
object_cast<Text>(object0),
|
||||
object_cast<Text>(object1)
|
||||
);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
return art::validation::main(argc, argv);
|
||||
}
|
||||
72
art/paperback/carousel/real.cxx
Normal file
72
art/paperback/carousel/real.cxx
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <art/paperback/carousel/real.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
Real::
|
||||
Real()
|
||||
{}
|
||||
|
||||
Real::
|
||||
Real(double value)
|
||||
: _data{value}
|
||||
{}
|
||||
|
||||
Real::
|
||||
Real(Real const& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Real::
|
||||
Real(Real&& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Real::
|
||||
~Real() noexcept
|
||||
{}
|
||||
|
||||
double const&
|
||||
Real::
|
||||
operator*() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
Real&
|
||||
Real::
|
||||
operator=(Real const& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
_data = other._data;
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Real&
|
||||
Real::
|
||||
operator=(Real&& other)
|
||||
{
|
||||
return *this = other;
|
||||
}
|
||||
|
||||
bool
|
||||
Real::
|
||||
operator==(Real const& other) const
|
||||
{
|
||||
return _data == other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Real::
|
||||
operator!=(Real const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
70
art/paperback/carousel/real.hxx
Normal file
70
art/paperback/carousel/real.hxx
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef art__paperback__carousel__real_hxx_
|
||||
#define art__paperback__carousel__real_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Represents a COS real.
|
||||
///
|
||||
class Real
|
||||
: public Object_model::Value_base
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
Real();
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Real(double);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Real(Real const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Real(Real&&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Real() noexcept;
|
||||
|
||||
/// Access real.
|
||||
///
|
||||
double const&
|
||||
operator*() const;
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Real&
|
||||
operator=(Real const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Real&
|
||||
operator=(Real&&);
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator==(Real const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator!=(Real const&) const;
|
||||
|
||||
private:
|
||||
double _data{};
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
173
art/paperback/carousel/stream.cxx
Normal file
173
art/paperback/carousel/stream.cxx
Normal file
@@ -0,0 +1,173 @@
|
||||
#include <art/paperback/carousel/stream.hxx>
|
||||
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
#include <art/paperback/carousel/integer.hxx>
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
struct Stream::Internal
|
||||
{
|
||||
Internal()
|
||||
{}
|
||||
|
||||
stringstream data;
|
||||
|
||||
};
|
||||
|
||||
/// \class Stream
|
||||
///
|
||||
/// A stream is a special COS object type containing a data stream
|
||||
/// of bytes. A stream object is always an indirect object.
|
||||
///
|
||||
|
||||
Stream::
|
||||
Stream()
|
||||
: _internal{new Internal{}}
|
||||
{}
|
||||
|
||||
Stream::
|
||||
Stream(Stream const& other)
|
||||
: _internal{new Internal{}}
|
||||
{
|
||||
_internal->data.str(other._internal->data.str());
|
||||
}
|
||||
|
||||
Stream::
|
||||
Stream(Stream&& other)
|
||||
: _internal{new Internal{}}
|
||||
{
|
||||
_internal->data.str(other._internal->data.str());
|
||||
}
|
||||
|
||||
Stream::
|
||||
~Stream() noexcept
|
||||
{}
|
||||
|
||||
void
|
||||
Stream::
|
||||
clear()
|
||||
{
|
||||
_internal->data.str(string{});
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary
|
||||
Stream::
|
||||
as_dictionary() const
|
||||
{
|
||||
_internal->data.seekg(0, std::ios::end);
|
||||
auto size = _internal->data.tellg();
|
||||
|
||||
Dictionary dictionary;
|
||||
|
||||
dictionary.insert(
|
||||
"Length",
|
||||
Integer{static_cast<int64_t>(size)}
|
||||
);
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
string
|
||||
Stream::
|
||||
str() const
|
||||
{
|
||||
return _internal->data.str();
|
||||
}
|
||||
|
||||
std::basic_streambuf<char>*
|
||||
Stream::
|
||||
rdbuf() const
|
||||
{
|
||||
_internal->data.seekg(0);
|
||||
return _internal->data.rdbuf();
|
||||
}
|
||||
|
||||
iostream*
|
||||
Stream::
|
||||
operator->()
|
||||
{
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
|
||||
return &_internal->data;
|
||||
}
|
||||
|
||||
iostream const*
|
||||
Stream::
|
||||
operator->() const
|
||||
{
|
||||
return &_internal->data;
|
||||
}
|
||||
|
||||
iostream&
|
||||
Stream::
|
||||
operator*()
|
||||
{
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
|
||||
return _internal->data;
|
||||
}
|
||||
|
||||
iostream const&
|
||||
Stream::
|
||||
operator*() const
|
||||
{
|
||||
return _internal->data;
|
||||
}
|
||||
|
||||
Stream&
|
||||
Stream::
|
||||
operator=(Stream const& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
_internal->data.str(other._internal->data.str());
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Stream&
|
||||
Stream::
|
||||
operator=(Stream&& other)
|
||||
{
|
||||
return *this = other;
|
||||
}
|
||||
|
||||
bool
|
||||
Stream::
|
||||
operator==(Stream const& other) const
|
||||
{
|
||||
return _internal->data.str() == other._internal->data.str();
|
||||
}
|
||||
|
||||
bool
|
||||
Stream::
|
||||
operator!=(Stream const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
/// \param object The object containing a stream.
|
||||
/// \return Returns a reference to the iostream.
|
||||
///
|
||||
iostream&
|
||||
stream_cast(Object& object)
|
||||
{
|
||||
return *object_cast<Stream>(object);
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
107
art/paperback/carousel/stream.hxx
Normal file
107
art/paperback/carousel/stream.hxx
Normal file
@@ -0,0 +1,107 @@
|
||||
#ifndef art__paperback__carousel__stream_hxx_
|
||||
#define art__paperback__carousel__stream_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Represents a COS stream.
|
||||
///
|
||||
class Stream
|
||||
: public Object_model::Value_base
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
Stream();
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Stream(Stream const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Stream(Stream&&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Stream() noexcept;
|
||||
|
||||
/// Clear stream.
|
||||
///
|
||||
void
|
||||
clear();
|
||||
|
||||
/// Returns the stream metadata as a dictionary.
|
||||
///
|
||||
Dictionary
|
||||
as_dictionary() const;
|
||||
|
||||
/// Access the buffer as a string.
|
||||
///
|
||||
string
|
||||
str() const;
|
||||
|
||||
/// Access buffer.
|
||||
///
|
||||
std::basic_streambuf<char>*
|
||||
rdbuf() const;
|
||||
|
||||
/// Access data stream.
|
||||
///
|
||||
iostream*
|
||||
operator->();
|
||||
|
||||
/// Access data stream.
|
||||
///
|
||||
iostream const*
|
||||
operator->() const;
|
||||
|
||||
/// Access data stream.
|
||||
///
|
||||
iostream&
|
||||
operator*();
|
||||
|
||||
/// Access data stream.
|
||||
///
|
||||
iostream const&
|
||||
operator*() const;
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Stream&
|
||||
operator=(Stream const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Stream&
|
||||
operator=(Stream&&);
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator==(Stream const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator!=(Stream const&) const;
|
||||
|
||||
private:
|
||||
struct Internal;
|
||||
unique_ptr<Internal> _internal;
|
||||
|
||||
};
|
||||
|
||||
/// Access stream object.
|
||||
///
|
||||
iostream&
|
||||
stream_cast(Object&);
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
134
art/paperback/carousel/text.cxx
Normal file
134
art/paperback/carousel/text.cxx
Normal file
@@ -0,0 +1,134 @@
|
||||
#include <art/paperback/carousel/text.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// \class Text
|
||||
///
|
||||
/// The Text class does not impose any limit on the length of the
|
||||
/// text string.
|
||||
|
||||
Text::
|
||||
Text(Convention convention)
|
||||
: _convention{convention}
|
||||
{}
|
||||
|
||||
Text::
|
||||
Text(string data, Convention convention)
|
||||
: _convention{convention},
|
||||
_data{std::move(data)}
|
||||
{}
|
||||
|
||||
Text::
|
||||
Text(char const* data, Convention convention)
|
||||
: _convention{convention},
|
||||
_data{data}
|
||||
{}
|
||||
|
||||
Text::
|
||||
Text(Text const& other)
|
||||
: _data{other._data}
|
||||
{}
|
||||
|
||||
Text::
|
||||
Text(Text&& other)
|
||||
: _data{std::move(other._data)}
|
||||
{}
|
||||
|
||||
Text::
|
||||
~Text() noexcept
|
||||
{}
|
||||
|
||||
Text::Convention
|
||||
Text::
|
||||
convention() const
|
||||
{
|
||||
return _convention;
|
||||
}
|
||||
|
||||
string const&
|
||||
Text::
|
||||
operator*() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
string const*
|
||||
Text::
|
||||
operator->() const
|
||||
{
|
||||
return &_data;
|
||||
}
|
||||
|
||||
Text&
|
||||
Text::
|
||||
operator=(Text const& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
_convention = other._convention;
|
||||
_data = other._data;
|
||||
|
||||
if (auto ptr = owner(); ptr) {
|
||||
ptr->mark_as_modified();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Text&
|
||||
Text::
|
||||
operator=(Text&& other)
|
||||
{
|
||||
return *this = other;
|
||||
}
|
||||
|
||||
/// This function only compares the string value of \c this with \a
|
||||
/// other. The text convention used by either string is ignored.
|
||||
///
|
||||
bool
|
||||
Text::
|
||||
operator==(Text const& other) const
|
||||
{
|
||||
return _data == other._data;
|
||||
}
|
||||
|
||||
/// This function only compares the string value of \c this with \a
|
||||
/// other. The text convention used by either string is ignored.
|
||||
///
|
||||
bool
|
||||
Text::
|
||||
operator!=(Text const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool
|
||||
Text::
|
||||
operator<(Text const& other) const
|
||||
{
|
||||
return _data < other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Text::
|
||||
operator<=(Text const& other) const
|
||||
{
|
||||
return _data <= other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Text::
|
||||
operator>(Text const& other) const
|
||||
{
|
||||
return _data > other._data;
|
||||
}
|
||||
|
||||
bool
|
||||
Text::
|
||||
operator>=(Text const& other) const
|
||||
{
|
||||
return _data >= other._data;
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
131
art/paperback/carousel/text.hxx
Normal file
131
art/paperback/carousel/text.hxx
Normal file
@@ -0,0 +1,131 @@
|
||||
#ifndef art__paperback__carousel__text_hxx_
|
||||
#define art__paperback__carousel__text_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// Represents COS text.
|
||||
///
|
||||
class Text
|
||||
: public Object_model::Value_base
|
||||
{
|
||||
public:
|
||||
/// Enumeration of text conventions.
|
||||
///
|
||||
/// See Section 3.2.3, "String Objects" in the PDF-1.4 specification
|
||||
/// for details about text strings and their representation.
|
||||
///
|
||||
enum class Convention
|
||||
{
|
||||
/// Indicates the literal convention. When used, the text string
|
||||
/// will be written verbatim to the output file, enclosed in
|
||||
/// parentheses.
|
||||
///
|
||||
/// Special characters will be written as escape-sequences.
|
||||
///
|
||||
literal,
|
||||
|
||||
/// Indicates the hexadecimal convention. When used, the text
|
||||
/// string will be written as a sequence of hexadecimal digits,
|
||||
/// enclosed within angle brackets.
|
||||
///
|
||||
/// The size of the written data will be double the length of
|
||||
/// the text string when this convention is used.
|
||||
///
|
||||
hexadecimal
|
||||
|
||||
};
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Text(Convention = Convention::literal);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Text(string, Convention = Convention::literal);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Text(char const*, Convention = Convention::literal);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Text(Text const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Text(Text&&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Text() noexcept;
|
||||
|
||||
/// Get the text convention used for this object.
|
||||
///
|
||||
Convention
|
||||
convention() const;
|
||||
|
||||
/// Access text string.
|
||||
///
|
||||
string const&
|
||||
operator*() const;
|
||||
|
||||
/// Access text string.
|
||||
///
|
||||
string const*
|
||||
operator->() const;
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Text&
|
||||
operator=(Text const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Text&
|
||||
operator=(Text&&);
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator==(Text const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator!=(Text const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator<(Text const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator<=(Text const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator>(Text const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator>=(Text const&) const;
|
||||
|
||||
private:
|
||||
Convention _convention;
|
||||
string _data;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
49
art/paperback/carousel/undefined.cxx
Normal file
49
art/paperback/carousel/undefined.cxx
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <art/paperback/carousel/undefined.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
Undefined::
|
||||
Undefined()
|
||||
{}
|
||||
|
||||
Undefined::
|
||||
Undefined(Undefined const&)
|
||||
{}
|
||||
|
||||
Undefined::
|
||||
Undefined(Undefined&&)
|
||||
{}
|
||||
|
||||
Undefined::
|
||||
~Undefined() noexcept
|
||||
{}
|
||||
|
||||
Undefined&
|
||||
Undefined::
|
||||
operator=(Undefined const&)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
Undefined&
|
||||
Undefined::
|
||||
operator=(Undefined&&)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
Undefined::
|
||||
operator==(Undefined const&) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Undefined::
|
||||
operator!=(Undefined const&) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
56
art/paperback/carousel/undefined.hxx
Normal file
56
art/paperback/carousel/undefined.hxx
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifndef art__paperback__carousel__undefined_hxx_
|
||||
#define art__paperback__carousel__undefined_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
class Undefined
|
||||
: public Object_model::Value_base
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
Undefined();
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Undefined(Undefined const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Undefined(Undefined&&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Undefined() noexcept;
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Undefined&
|
||||
operator=(Undefined const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
Undefined&
|
||||
operator=(Undefined&&);
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator==(Undefined const&) const;
|
||||
|
||||
/// Comparison.
|
||||
///
|
||||
bool
|
||||
operator!=(Undefined const&) const;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
487
art/paperback/carousel/writer.cxx
Normal file
487
art/paperback/carousel/writer.cxx
Normal file
@@ -0,0 +1,487 @@
|
||||
#include <art/paperback/carousel/writer.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
|
||||
#include <art/paperback/carousel/array.hxx>
|
||||
#include <art/paperback/carousel/boolean.hxx>
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
#include <art/paperback/carousel/integer.hxx>
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
#include <art/paperback/carousel/real.hxx>
|
||||
#include <art/paperback/carousel/stream.hxx>
|
||||
#include <art/paperback/carousel/text.hxx>
|
||||
|
||||
#include <art/paperback/except.hxx>
|
||||
|
||||
#include <art/unicode/writer.hxx>
|
||||
#include <art/unicode/utf8-encoder.hxx>
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
/// \param output The output stream to write to.
|
||||
/// \param major The major version of the document to write.
|
||||
/// \param minor The minor version of the document to write.
|
||||
///
|
||||
Writer::
|
||||
Writer(ostream& output, int major, int minor)
|
||||
: _output{output},
|
||||
_major{major},
|
||||
_minor{minor}
|
||||
{}
|
||||
|
||||
/// \return Returns a reference to the underlying output stream.
|
||||
///
|
||||
ostream&
|
||||
Writer::
|
||||
output()
|
||||
{
|
||||
return _output;
|
||||
}
|
||||
|
||||
/// This function will automatically write a binary comment immediately
|
||||
/// following the COS-file header, as per the recommendation of the PDF-1.4
|
||||
/// specification.
|
||||
///
|
||||
/// \param header The COS-file header.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
write_header(string const& header)
|
||||
{
|
||||
output() << '%' << header << "\r\n" << "%\x80\xff\x80\xff\r\n";
|
||||
}
|
||||
|
||||
/// \param identity The identity of the object.
|
||||
/// \param container The object container.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
write_object(Identity const& identity,
|
||||
Object_model::Container_base const& container)
|
||||
{
|
||||
output() << identity.index << ' '
|
||||
<< identity.generation << " obj\r\n";
|
||||
|
||||
dispatch_emit(container);
|
||||
|
||||
if (container.type() == typeid(Stream)) {
|
||||
auto str = container.get<Stream>().str();
|
||||
|
||||
output() << "\r\nstream\r\n";
|
||||
output().write(str.c_str(), str.size());
|
||||
output() << "\r\nendstream";
|
||||
}
|
||||
|
||||
output() << "\r\nendobj\r\n";
|
||||
}
|
||||
|
||||
void
|
||||
Writer::
|
||||
begin_xref()
|
||||
{
|
||||
output() << "xref\r\n";
|
||||
}
|
||||
|
||||
/// \param index The first index of this chunk.
|
||||
/// \param count The size of this chunk.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
begin_xref_chunk(Index index, uint16_t count)
|
||||
{
|
||||
output() << index << ' ' << count << "\r\n";
|
||||
}
|
||||
|
||||
/// \param offset The offset of the entry.
|
||||
/// \param generation The generation of the entry.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
write_xref_used_entry(streamoff offset, Generation generation)
|
||||
{
|
||||
using std::setfill;
|
||||
using std::setw;
|
||||
|
||||
std::ostringstream str;
|
||||
str << setw(10) << setfill('0') << offset << ' ';
|
||||
str << setw(5) << setfill('0') << generation << " n\r\n";
|
||||
|
||||
output() << str.str();
|
||||
}
|
||||
|
||||
/// \param next The index of the next entry.
|
||||
/// \param generation The next generation number of the entry.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
write_xref_free_entry(Index next, Generation generation)
|
||||
{
|
||||
using std::setfill;
|
||||
using std::setw;
|
||||
|
||||
std::ostringstream str;
|
||||
str << setw(10) << setfill('0') << next << ' ';
|
||||
str << setw(5) << setfill('0') << generation << " f\r\n";
|
||||
|
||||
output() << str.str();
|
||||
}
|
||||
|
||||
/// \param dictionary The trailer dictionary.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
write_trailer(Dictionary const& dictionary)
|
||||
{
|
||||
output() << "trailer\r\n";
|
||||
emit(dictionary);
|
||||
output() << "\r\n";
|
||||
}
|
||||
|
||||
/// \param xref_offset The offset of the cross-reference table.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
write_eof(streamoff xref_offset)
|
||||
{
|
||||
output() << "startxref\r\n";
|
||||
output() << xref_offset << "\r\n";
|
||||
output() << "%%EOF\r\n";
|
||||
}
|
||||
|
||||
/// \param index The object reference index.
|
||||
/// \param generation The object reference generation.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
emit_reference(Identity const& identity)
|
||||
{
|
||||
output() << identity.index << ' ' << identity.generation << ' ' << 'R';
|
||||
}
|
||||
|
||||
void
|
||||
Writer::
|
||||
emit(Undefined const&)
|
||||
{
|
||||
output() << "null";
|
||||
}
|
||||
|
||||
/// \param boolean The boolean to write.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
emit(Boolean const& boolean)
|
||||
{
|
||||
output() << (*boolean ? "true" : "false");
|
||||
}
|
||||
|
||||
/// \param integer The integer to write.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
emit(Integer const& integer)
|
||||
{
|
||||
output() << *integer;
|
||||
}
|
||||
|
||||
/// \param real The real to write.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
emit(Real const& real)
|
||||
{
|
||||
stringstream str;
|
||||
str.imbue(std::locale::classic());
|
||||
str << std::fixed << *real;
|
||||
output() << str.str();
|
||||
}
|
||||
|
||||
/// \param text The text to write.
|
||||
///
|
||||
void
|
||||
Writer::
|
||||
emit(Text const& text)
|
||||
{
|
||||
auto to_hex = [](char c) -> char
|
||||
{
|
||||
return (c < 10) ? ('0' + c) : ('a' + (c - 10));
|
||||
};
|
||||
|
||||
switch (text.convention()) {
|
||||
case Text::Convention::literal: {
|
||||
output() << '(';
|
||||
|
||||
for (auto const& j : *text) {
|
||||
switch (j) {
|
||||
case '\n':
|
||||
output() << "\\n";
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
output() << "\\r";
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
output() << "\\t";
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
output() << "\\b";
|
||||
break;
|
||||
|
||||
case '\f':
|
||||
output() << "\\f";
|
||||
break;
|
||||
|
||||
case '(':
|
||||
output() << "\\(";
|
||||
break;
|
||||
|
||||
case ')':
|
||||
output() << "\\)";
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
output() << "\\\\";
|
||||
break;
|
||||
|
||||
default:
|
||||
output() << j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output() << ')';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case Text::Convention::hexadecimal: {
|
||||
output() << '<';
|
||||
|
||||
for (auto const& j : *text) {
|
||||
char high = (j & 0xF0) >> 4;
|
||||
char low = (j & 0x0F);
|
||||
|
||||
output() << to_hex(high) << to_hex(low);
|
||||
}
|
||||
|
||||
output() << '>';
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
raise<Internal_error>{} << "unhandled text convention";
|
||||
}
|
||||
|
||||
void
|
||||
Writer::
|
||||
emit(Name const& name)
|
||||
{
|
||||
if (_major == 1 && _minor < 2) {
|
||||
output() << *name;
|
||||
return;
|
||||
}
|
||||
|
||||
auto to_hex = [](char c) -> char
|
||||
{
|
||||
return (c < 10) ? ('0' + c) : ('a' + (c - 10));
|
||||
};
|
||||
|
||||
output() << '/';
|
||||
|
||||
for (auto const& j : *name) {
|
||||
if (j == '#') {
|
||||
output() << "#23";
|
||||
}
|
||||
else if (33 <= j && j <= 126) {
|
||||
output() << j;
|
||||
}
|
||||
else {
|
||||
char high = (j & 0xF0) >> 4;
|
||||
char low = (j & 0x0F);
|
||||
|
||||
output() << "#" << to_hex(high) << to_hex(low);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Writer::
|
||||
emit(Array const& array)
|
||||
{
|
||||
output() << "[ ";
|
||||
|
||||
auto it = array.begin();
|
||||
|
||||
if (it == array.end()) {
|
||||
output() << "]";
|
||||
return;
|
||||
}
|
||||
|
||||
auto base_indent = _indent;
|
||||
_indent += 1;
|
||||
|
||||
dispatch_emit(*it);
|
||||
++it;
|
||||
|
||||
for (; it != array.end(); ++it) {
|
||||
new_line();
|
||||
output() << ' ';
|
||||
dispatch_emit(*it);
|
||||
}
|
||||
|
||||
_indent = base_indent;
|
||||
|
||||
new_line();
|
||||
|
||||
output() << ']';
|
||||
}
|
||||
|
||||
void
|
||||
Writer::
|
||||
emit(Dictionary const& dictionary)
|
||||
{
|
||||
output() << "<< ";
|
||||
|
||||
auto it = dictionary.begin();
|
||||
|
||||
if (it == dictionary.end()) {
|
||||
output() << ">>";
|
||||
return;
|
||||
}
|
||||
|
||||
auto base_indent = _indent;
|
||||
|
||||
emit(it->first);
|
||||
output() << ' ';
|
||||
_indent = base_indent + it->first->length() + 5;
|
||||
dispatch_emit(it->second);
|
||||
++it;
|
||||
_indent = base_indent;
|
||||
new_line();
|
||||
|
||||
for (; it != dictionary.end(); ++it) {
|
||||
output() << " ";
|
||||
emit(it->first);
|
||||
output() << " ";
|
||||
_indent = base_indent + it->first->length() + 5;
|
||||
dispatch_emit(it->second);
|
||||
_indent = base_indent;
|
||||
new_line();
|
||||
}
|
||||
|
||||
_indent = base_indent;
|
||||
|
||||
output() << ">>";
|
||||
}
|
||||
|
||||
void
|
||||
Writer::
|
||||
dispatch_emit(Object const& object)
|
||||
{
|
||||
// If the object is an indirect object we emit a reference (N N R)
|
||||
// instead of the object value.
|
||||
//
|
||||
if (object.is_reference()) {
|
||||
emit_reference(object.identity());
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_emit(object.container());
|
||||
}
|
||||
|
||||
void
|
||||
Writer::
|
||||
dispatch_emit(Object_model::Container_base const& container)
|
||||
{
|
||||
|
||||
// Else emit the object directly.
|
||||
//
|
||||
struct dispatcher : Visitor,
|
||||
Basic_visitor<Undefined >,
|
||||
Basic_visitor<Boolean>,
|
||||
Basic_visitor<Integer>,
|
||||
Basic_visitor<Real>,
|
||||
Basic_visitor<Text>,
|
||||
Basic_visitor<Name>,
|
||||
Basic_visitor<Array>,
|
||||
Basic_visitor<Dictionary>,
|
||||
Basic_visitor<Stream> {
|
||||
Writer& w;
|
||||
|
||||
explicit
|
||||
dispatcher(Writer& w)
|
||||
: w{w}
|
||||
{}
|
||||
|
||||
void
|
||||
visit(Undefined const& v) override
|
||||
{
|
||||
w.emit(v);
|
||||
}
|
||||
|
||||
void
|
||||
visit(Boolean const& v) override
|
||||
{
|
||||
w.emit(v);
|
||||
}
|
||||
|
||||
void
|
||||
visit(Integer const& v) override
|
||||
{
|
||||
w.emit(v);
|
||||
}
|
||||
|
||||
void
|
||||
visit(Real const& v) override
|
||||
{
|
||||
w.emit(v);
|
||||
}
|
||||
|
||||
void
|
||||
visit(Text const& v) override
|
||||
{
|
||||
w.emit(v);
|
||||
}
|
||||
|
||||
void
|
||||
visit(Name const& v) override
|
||||
{
|
||||
w.emit(v);
|
||||
}
|
||||
|
||||
void
|
||||
visit(Array const& v) override
|
||||
{
|
||||
w.emit(v);
|
||||
}
|
||||
|
||||
void
|
||||
visit(Dictionary const& v) override
|
||||
{
|
||||
w.emit(v);
|
||||
}
|
||||
|
||||
void
|
||||
visit(Stream const& v) override
|
||||
{
|
||||
w.emit(v.as_dictionary());
|
||||
}
|
||||
};
|
||||
|
||||
dispatcher d{*this};
|
||||
container.do_accept(d);
|
||||
}
|
||||
|
||||
void
|
||||
Writer::
|
||||
new_line()
|
||||
{
|
||||
output() << "\r\n" << std::string(_indent, ' ');
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
142
art/paperback/carousel/writer.hxx
Normal file
142
art/paperback/carousel/writer.hxx
Normal file
@@ -0,0 +1,142 @@
|
||||
#ifndef art__paperback__carousel__writer_hxx_
|
||||
#define art__paperback__carousel__writer_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object-model.hxx>
|
||||
|
||||
namespace Art::Paperback::Carousel
|
||||
{
|
||||
|
||||
class Writer
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
Writer(ostream&, int, int);
|
||||
|
||||
/// Access the underlying output stream.
|
||||
///
|
||||
ostream&
|
||||
output();
|
||||
|
||||
/// Write a COS-file header.
|
||||
///
|
||||
void
|
||||
write_header(string const&);
|
||||
|
||||
/// Write an object.
|
||||
///
|
||||
void
|
||||
write_object(Identity const&, Object_model::Container_base const&);
|
||||
|
||||
/// Begin a new cross-reference table.
|
||||
///
|
||||
void
|
||||
begin_xref();
|
||||
|
||||
/// Begin a new cross-reference table chunk.
|
||||
///
|
||||
void
|
||||
begin_xref_chunk(Index, uint16_t);
|
||||
|
||||
/// Write a used entry.
|
||||
///
|
||||
void
|
||||
write_xref_used_entry(streamoff, Generation);
|
||||
|
||||
/// Write a free entry.
|
||||
///
|
||||
void
|
||||
write_xref_free_entry(Index, Generation);
|
||||
|
||||
/// Write a COS-file trailer.
|
||||
///
|
||||
void
|
||||
write_trailer(Dictionary const&);
|
||||
|
||||
/// Write an EOF marker.
|
||||
///
|
||||
void
|
||||
write_eof(streamoff);
|
||||
|
||||
private:
|
||||
/// Emit a reference to an indirect object.
|
||||
///
|
||||
void
|
||||
emit_reference(Identity const&);
|
||||
|
||||
/// Emit undefined.
|
||||
///
|
||||
void
|
||||
emit(Undefined const&);
|
||||
|
||||
/// Emit a boolean object.
|
||||
///
|
||||
void
|
||||
emit(Boolean const&);
|
||||
|
||||
/// Emit an integer object.
|
||||
///
|
||||
void
|
||||
emit(Integer const&);
|
||||
|
||||
/// Emit a real object.
|
||||
///
|
||||
void
|
||||
emit(Real const&);
|
||||
|
||||
/// Emit a text object.
|
||||
///
|
||||
void
|
||||
emit(Text const&);
|
||||
|
||||
/// Emit a name object.
|
||||
///
|
||||
void
|
||||
emit(Name const&);
|
||||
|
||||
/// Emit an array object.
|
||||
///
|
||||
void
|
||||
emit(Array const&);
|
||||
|
||||
/// Emit a dictionary object.
|
||||
///
|
||||
void
|
||||
emit(Dictionary const&);
|
||||
|
||||
/// Dispatch the emission of an object.
|
||||
///
|
||||
void
|
||||
dispatch_emit(Object const&);
|
||||
|
||||
/// Dispatch the emission of an object.
|
||||
///
|
||||
void
|
||||
dispatch_emit(Object_model::Container_base const&);
|
||||
|
||||
/// Write a new line marker.
|
||||
///
|
||||
void
|
||||
new_line();
|
||||
|
||||
private:
|
||||
Writer(Writer const&) = delete;
|
||||
Writer(Writer&&) = delete;
|
||||
Writer& operator=(Writer const&) = delete;
|
||||
Writer& operator=(Writer&&) = delete;
|
||||
|
||||
private:
|
||||
ostream& _output;
|
||||
int _major{};
|
||||
int _minor{};
|
||||
|
||||
string::size_type _indent{};
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Carousel
|
||||
|
||||
#endif
|
||||
229
art/paperback/document-information.cxx
Normal file
229
art/paperback/document-information.cxx
Normal file
@@ -0,0 +1,229 @@
|
||||
#include <art/paperback/document-information.hxx>
|
||||
|
||||
#include <art/paperback/document.hxx>
|
||||
#include <art/paperback/except.hxx>
|
||||
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
#include <art/paperback/carousel/file.hxx>
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
#include <art/paperback/carousel/text.hxx>
|
||||
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
/// Holds document information internal data.
|
||||
///
|
||||
struct Document_information::Internal
|
||||
{
|
||||
Internal(Document& document)
|
||||
: document{document},
|
||||
data{document.file().info()}
|
||||
{}
|
||||
|
||||
Document& document;
|
||||
Carousel::Dictionary& data;
|
||||
|
||||
};
|
||||
|
||||
Document_information::
|
||||
Document_information(Create_new const&,
|
||||
Document& document)
|
||||
: internal{new Internal{document}}
|
||||
{}
|
||||
|
||||
Document_information::
|
||||
~Document_information() noexcept
|
||||
{}
|
||||
|
||||
Document&
|
||||
Document_information::
|
||||
document()
|
||||
{
|
||||
return internal->document;
|
||||
}
|
||||
|
||||
Document const&
|
||||
Document_information::
|
||||
document() const
|
||||
{
|
||||
return internal->document;
|
||||
}
|
||||
|
||||
/// Returns the title of the document, if available.
|
||||
///
|
||||
std::optional<std::string>
|
||||
Document_information::
|
||||
title() const
|
||||
{
|
||||
document().check_minimum_version(1, 1);
|
||||
|
||||
if (internal->data.contains("Title")) {
|
||||
return *object_cast<Carousel::Text>(internal->data.at("Title"));
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
/// If \a value is \c nullopt the title is removed.
|
||||
///
|
||||
/// \param value The new title.
|
||||
///
|
||||
void
|
||||
Document_information::
|
||||
set_title(std::optional<std::string> value)
|
||||
{
|
||||
document().check_minimum_version(1, 1);
|
||||
|
||||
if (value) {
|
||||
internal->data.insert("Title", Carousel::Text{*value});
|
||||
}
|
||||
else {
|
||||
internal->data.erase("Title");
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
Document_information::
|
||||
author() const
|
||||
{
|
||||
if (internal->data.contains("Author")) {
|
||||
return *object_cast<Carousel::Text>(internal->data.at("Author"));
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
/// If \a value is \c nullopt the author is removed.
|
||||
///
|
||||
/// \param value The new author.
|
||||
///
|
||||
void
|
||||
Document_information::
|
||||
set_author(std::optional<std::string> value)
|
||||
{
|
||||
if (value) {
|
||||
internal->data.insert("Author", Carousel::Text{*value});
|
||||
}
|
||||
else {
|
||||
internal->data.erase("Author");
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
Document_information::
|
||||
subject() const
|
||||
{
|
||||
document().check_minimum_version(1, 1);
|
||||
|
||||
if (internal->data.contains("Subject")) {
|
||||
return *object_cast<Carousel::Text>(internal->data.at("Subject"));
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
/// If \a value is \c nullopt the subject is removed.
|
||||
///
|
||||
/// \param value The new subject.
|
||||
///
|
||||
void
|
||||
Document_information::
|
||||
set_subject(std::optional<std::string> value)
|
||||
{
|
||||
document().check_minimum_version(1, 1);
|
||||
|
||||
if (value) {
|
||||
internal->data.insert("Subject", Carousel::Text{*value});
|
||||
}
|
||||
else {
|
||||
internal->data.erase("Subject");
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
Document_information::
|
||||
keywords() const
|
||||
{
|
||||
document().check_minimum_version(1, 1);
|
||||
|
||||
if (internal->data.contains("Keywords")) {
|
||||
return *object_cast<Carousel::Text>(internal->data.at("Keywords"));
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
/// If \a value is \c nullopt the keywords are removed.
|
||||
///
|
||||
/// \param value The new keywords.
|
||||
///
|
||||
void
|
||||
Document_information::
|
||||
set_keywords(std::optional<std::string> value)
|
||||
{
|
||||
document().check_minimum_version(1, 1);
|
||||
|
||||
if (value) {
|
||||
internal->data.insert("Keywords", Carousel::Text{*value});
|
||||
}
|
||||
else {
|
||||
internal->data.erase("Keywords");
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
Document_information::
|
||||
creator() const
|
||||
{
|
||||
if (internal->data.contains("Creator")) {
|
||||
return *object_cast<Carousel::Text>(internal->data.at("Creator"));
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
/// If \a value is \c nullopt the creator is removed.
|
||||
///
|
||||
/// \param value The new creator.
|
||||
///
|
||||
void
|
||||
Document_information::
|
||||
set_creator(std::optional<std::string> value)
|
||||
{
|
||||
if (value) {
|
||||
internal->data.insert("Creator", Carousel::Text{*value});
|
||||
}
|
||||
else {
|
||||
internal->data.erase("Creator");
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
Document_information::
|
||||
producer() const
|
||||
{
|
||||
if (internal->data.contains("Producer")) {
|
||||
return *object_cast<Carousel::Text>(internal->data.at("Producer"));
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
/// If \a value is \c nullopt the producer is removed.
|
||||
///
|
||||
/// \param value The new producer.
|
||||
///
|
||||
void
|
||||
Document_information::
|
||||
set_producer(std::optional<std::string> value)
|
||||
{
|
||||
if (value) {
|
||||
internal->data.insert("Producer", Carousel::Text{*value});
|
||||
}
|
||||
else {
|
||||
internal->data.erase("Producer");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback
|
||||
108
art/paperback/document-information.hxx
Normal file
108
art/paperback/document-information.hxx
Normal file
@@ -0,0 +1,108 @@
|
||||
#ifndef art__paperback__document_information_hxx_
|
||||
#define art__paperback__document_information_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
class Document_information
|
||||
{
|
||||
public:
|
||||
struct Create_new {};
|
||||
static constexpr Create_new const create_new{};
|
||||
|
||||
Document_information(Create_new const&, Document&);
|
||||
|
||||
~Document_information() noexcept;
|
||||
|
||||
/// Access the owning document.
|
||||
///
|
||||
Document&
|
||||
document();
|
||||
|
||||
/// Access the owning document.
|
||||
///
|
||||
Document const&
|
||||
document() const;
|
||||
|
||||
/// Get the object for the document information.
|
||||
///
|
||||
Carousel::Object
|
||||
object();
|
||||
|
||||
/// Access the title.
|
||||
///
|
||||
optional<string>
|
||||
title() const;
|
||||
|
||||
/// Set or remove the title.
|
||||
///
|
||||
void
|
||||
set_title(optional<string>);
|
||||
|
||||
/// Access the author.
|
||||
///
|
||||
optional<string>
|
||||
author() const;
|
||||
|
||||
/// Set or remove the author.
|
||||
///
|
||||
void
|
||||
set_author(optional<string>);
|
||||
|
||||
/// Access the subject.
|
||||
///
|
||||
optional<string>
|
||||
subject() const;
|
||||
|
||||
/// Set or remove the subject.
|
||||
///
|
||||
void
|
||||
set_subject(optional<string>);
|
||||
|
||||
/// Access the keywords.
|
||||
///
|
||||
optional<string>
|
||||
keywords() const;
|
||||
|
||||
/// Set or remove the keywords.
|
||||
///
|
||||
void
|
||||
set_keywords(optional<string>);
|
||||
|
||||
/// Access the creator.
|
||||
///
|
||||
optional<string>
|
||||
creator() const;
|
||||
|
||||
/// Set or remove the creator.
|
||||
///
|
||||
void
|
||||
set_creator(optional<string>);
|
||||
|
||||
/// Access the producer.
|
||||
///
|
||||
optional<string>
|
||||
producer() const;
|
||||
|
||||
/// Set or remove the producer.
|
||||
///
|
||||
void
|
||||
set_producer(optional<string>);
|
||||
|
||||
private:
|
||||
Document_information(Document_information const&) = delete;
|
||||
Document_information(Document_information&&) = delete;
|
||||
Document_information& operator=(Document_information const&) = delete;
|
||||
Document_information& operator=(Document_information&&) = delete;
|
||||
|
||||
struct Internal;
|
||||
unique_ptr<Internal> internal;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback
|
||||
|
||||
#endif
|
||||
141
art/paperback/document.cxx
Normal file
141
art/paperback/document.cxx
Normal file
@@ -0,0 +1,141 @@
|
||||
#include <art/paperback/document.hxx>
|
||||
#include <art/paperback/except.hxx>
|
||||
|
||||
#include <art/paperback/document-information.hxx>
|
||||
|
||||
#include <art/paperback/internals/document-catalog.hxx>
|
||||
|
||||
#include <art/paperback/carousel/file.hxx>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
/// Helper to create a PDF-file header.
|
||||
///
|
||||
static
|
||||
string
|
||||
make_header(int major, int minor)
|
||||
{
|
||||
std::stringstream str;
|
||||
str << "PDF-" << major << '.' << minor;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
struct Document::Internal
|
||||
{
|
||||
Internal(iostream& ios, int major, int minor)
|
||||
: file{
|
||||
Carousel::File::create_new,
|
||||
ios,
|
||||
major,
|
||||
minor,
|
||||
make_header(major, minor),
|
||||
},
|
||||
major{major},
|
||||
minor{minor}
|
||||
{}
|
||||
|
||||
Carousel::File file;
|
||||
int major{};
|
||||
int minor{};
|
||||
|
||||
optional<Internals::Document_catalog> catalog;
|
||||
|
||||
optional<Document_information> info;
|
||||
|
||||
};
|
||||
|
||||
Document::
|
||||
Document(Create_new const&, iostream& ios, int major, int minor)
|
||||
: internal{new Internal{ios, major, minor}}
|
||||
{
|
||||
if (major < 1 || 1 < major) {
|
||||
throw invalid_argument{"unsupported major version"};
|
||||
}
|
||||
|
||||
if (minor < 1 || 4 < minor) {
|
||||
throw invalid_argument{"unsupported minor version"};
|
||||
}
|
||||
}
|
||||
|
||||
Document::
|
||||
~Document()
|
||||
{
|
||||
file().commit_revision();
|
||||
}
|
||||
|
||||
int
|
||||
Document::
|
||||
major() const
|
||||
{
|
||||
return internal->major;
|
||||
}
|
||||
|
||||
int
|
||||
Document::
|
||||
minor() const
|
||||
{
|
||||
return internal->minor;
|
||||
}
|
||||
|
||||
/// \param major The minimum required major version.
|
||||
/// \param minor The minimum required minor version.
|
||||
/// \throw Upgrade_required Thrown if the document does not meet
|
||||
/// the specified version requirements.
|
||||
void
|
||||
Document::
|
||||
check_minimum_version(int major, int minor) const
|
||||
{
|
||||
if (major > this->major()) {
|
||||
raise<Upgrade_required>{} << "document version upgrade required";
|
||||
}
|
||||
|
||||
if (minor > this->minor()) {
|
||||
raise<Upgrade_required>{} << "document version upgrade required";
|
||||
}
|
||||
}
|
||||
|
||||
Document_information&
|
||||
Document::
|
||||
information()
|
||||
{
|
||||
if (!internal->info) {
|
||||
internal->info.emplace(Document_information::create_new, *this);
|
||||
}
|
||||
|
||||
return *internal->info;
|
||||
}
|
||||
|
||||
/// \param properties The properties of the new page.
|
||||
///
|
||||
Page&
|
||||
Document::
|
||||
create_page(Page::Properties const& properties)
|
||||
{
|
||||
if (!internal->catalog) {
|
||||
internal->catalog.emplace(
|
||||
Internals::Document_catalog::create_new,
|
||||
*this
|
||||
);
|
||||
}
|
||||
|
||||
return internal->catalog->pages().create_page(properties);
|
||||
}
|
||||
|
||||
Carousel::File&
|
||||
Document::
|
||||
file()
|
||||
{
|
||||
return internal->file;
|
||||
}
|
||||
|
||||
void
|
||||
Document::
|
||||
flush()
|
||||
{
|
||||
internal->file.commit_revision();
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback
|
||||
77
art/paperback/document.hxx
Normal file
77
art/paperback/document.hxx
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef art__paperback__document_hxx_
|
||||
#define art__paperback__document_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
#include <art/paperback/page.hxx>
|
||||
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
class Document
|
||||
{
|
||||
public:
|
||||
struct Create_new {};
|
||||
|
||||
/// Tag used to indicate the creation of a new document.
|
||||
///
|
||||
static constexpr Create_new const create_new{};
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Document(Create_new const&, std::iostream&, int, int);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Document();
|
||||
|
||||
/// Get document major version.
|
||||
///
|
||||
int
|
||||
major() const;
|
||||
|
||||
/// Get document minor version.
|
||||
///
|
||||
int
|
||||
minor() const;
|
||||
|
||||
/// Check that the document meets minimum version requirements.
|
||||
///
|
||||
void
|
||||
check_minimum_version(int, int) const;
|
||||
|
||||
/// Get document information.
|
||||
///
|
||||
Document_information&
|
||||
information();
|
||||
|
||||
/// Create new page.
|
||||
///
|
||||
Page&
|
||||
create_page(Page::Properties const&);
|
||||
|
||||
/// Access the underlying COS file.
|
||||
///
|
||||
Carousel::File&
|
||||
file();
|
||||
|
||||
/// Flush current document state to output stream.
|
||||
///
|
||||
void
|
||||
flush();
|
||||
|
||||
private:
|
||||
Document(Document const&) = delete;
|
||||
Document(Document&&) = delete;
|
||||
Document& operator=(Document const&) = delete;
|
||||
Document& operator=(Document&&) = delete;
|
||||
|
||||
private:
|
||||
struct Internal;
|
||||
std::unique_ptr<Internal> internal;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback
|
||||
|
||||
#endif
|
||||
117
art/paperback/except.hxx
Normal file
117
art/paperback/except.hxx
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifndef art__paperback__exception_hxx_
|
||||
#define art__paperback__exception_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
|
||||
#include <source_location>
|
||||
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
/// Base class for errors.
|
||||
///
|
||||
class Fault
|
||||
: public runtime_error
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param origin The C++ source origin of the exception.
|
||||
/// \param what A description of the error.
|
||||
///
|
||||
Fault(std::source_location origin, string what)
|
||||
: runtime_error{std::move(what)},
|
||||
_origin{std::move(origin)}
|
||||
{}
|
||||
|
||||
/// Access the C++ source origin of the exception.
|
||||
///
|
||||
std::source_location const&
|
||||
origin() const;
|
||||
|
||||
private:
|
||||
std::source_location _origin;
|
||||
|
||||
};
|
||||
|
||||
/// Exception class used to indicate internal errors, typically the result
|
||||
/// of an insect hiding somewhere. If found, please be so kind and squash it
|
||||
/// mercilessly.
|
||||
///
|
||||
class Internal_error
|
||||
: public Fault
|
||||
{
|
||||
public:
|
||||
using Fault::Fault;
|
||||
|
||||
};
|
||||
|
||||
/// Exception class used to indicate too low document version.
|
||||
///
|
||||
class Upgrade_required
|
||||
: public Fault
|
||||
{
|
||||
public:
|
||||
using Fault::Fault;
|
||||
|
||||
};
|
||||
|
||||
/// Exception class used to indicate an invalid operation.
|
||||
///
|
||||
class Invalid_operation
|
||||
: public Fault
|
||||
{
|
||||
public:
|
||||
using Fault::Fault;
|
||||
|
||||
};
|
||||
|
||||
/// Helper to throw exceptions.
|
||||
///
|
||||
template<typename T>
|
||||
class raise
|
||||
{
|
||||
public:
|
||||
raise(std::source_location origin = std::source_location::current())
|
||||
: _origin{origin}
|
||||
{}
|
||||
|
||||
/// Copy-construction is prohibited.
|
||||
///
|
||||
raise(raise const&) = delete;
|
||||
|
||||
/// Move-construction is prohibited.
|
||||
///
|
||||
raise(raise&&) = delete;
|
||||
|
||||
[[noreturn]]
|
||||
~raise() noexcept(false)
|
||||
{
|
||||
throw T{_origin, _str.str()};
|
||||
}
|
||||
|
||||
/// Copy-assignment is prohibited.
|
||||
///
|
||||
raise& operator=(raise const&) = delete;
|
||||
|
||||
/// Move-assignment is prohibited.
|
||||
///
|
||||
raise& operator=(raise&&) = delete;
|
||||
|
||||
template<typename U>
|
||||
raise<T>&
|
||||
operator<<(U const& other)
|
||||
{
|
||||
_str << other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
std::source_location _origin;
|
||||
stringstream _str;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback
|
||||
|
||||
#endif
|
||||
122
art/paperback/forward.hxx
Normal file
122
art/paperback/forward.hxx
Normal file
@@ -0,0 +1,122 @@
|
||||
#ifndef art__paperback__forward_hxx_
|
||||
#define art__paperback__forward_hxx_
|
||||
|
||||
/// Primary paperback namespace.
|
||||
///
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
class Visitor;
|
||||
|
||||
template<typename>
|
||||
class Basic_visitor;
|
||||
|
||||
/// COS file format implementation namespace.
|
||||
///
|
||||
namespace Carousel
|
||||
{
|
||||
|
||||
class Object;
|
||||
|
||||
template<typename>
|
||||
bool
|
||||
is_of_type(Object const&);
|
||||
|
||||
template<typename T>
|
||||
T&
|
||||
object_cast(Object&);
|
||||
|
||||
template<typename T>
|
||||
T const&
|
||||
object_cast(Object const&);
|
||||
|
||||
Object
|
||||
clone(Object&);
|
||||
|
||||
void
|
||||
accept(Object&, Visitor&);
|
||||
|
||||
void
|
||||
accept(Object const&, Visitor&);
|
||||
|
||||
class Undefined;
|
||||
|
||||
class Array;
|
||||
class Boolean;
|
||||
class Dictionary;
|
||||
class Integer;
|
||||
class Name;
|
||||
class Real;
|
||||
class Text;
|
||||
|
||||
class Stream;
|
||||
|
||||
class Cross_reference;
|
||||
|
||||
class Writer;
|
||||
class File;
|
||||
|
||||
/// COS object model namespace.
|
||||
///
|
||||
/// This namespace contains classes implementing the COS object
|
||||
/// model (values, references, containers and owners).
|
||||
///
|
||||
namespace Object_model
|
||||
{
|
||||
|
||||
class abstract_value;
|
||||
template<typename>
|
||||
|
||||
class Value;
|
||||
|
||||
class Abstract;
|
||||
class Reference;
|
||||
|
||||
class Owner;
|
||||
|
||||
class Container_base;
|
||||
|
||||
template<typename>
|
||||
class Container;
|
||||
|
||||
} // namespace Object_model
|
||||
|
||||
} // namespace Carousel
|
||||
|
||||
/// Internal namespace.
|
||||
///
|
||||
/// This namespace contains internal classes and functions.
|
||||
///
|
||||
namespace Internals
|
||||
{
|
||||
|
||||
class Document_catalog;
|
||||
class Font_collection;
|
||||
class Page_tree;
|
||||
class Resource_collection;
|
||||
|
||||
} // namespace Internals
|
||||
|
||||
/// Graphics namespace.
|
||||
///
|
||||
/// Text, font and graphics related classes.
|
||||
///
|
||||
namespace Graphics
|
||||
{
|
||||
|
||||
class Canvas;
|
||||
|
||||
class Font;
|
||||
class Standard_font;
|
||||
|
||||
} // namespace Graphics
|
||||
|
||||
class Document_information;
|
||||
class Document;
|
||||
class Page;
|
||||
|
||||
class Rectangle;
|
||||
|
||||
} // namespace Art::Paperback
|
||||
|
||||
#endif
|
||||
462
art/paperback/graphics/canvas.cxx
Normal file
462
art/paperback/graphics/canvas.cxx
Normal file
@@ -0,0 +1,462 @@
|
||||
#include <art/paperback/graphics/canvas.hxx>
|
||||
#include <art/paperback/graphics/font.hxx>
|
||||
|
||||
#include <art/paperback/except.hxx>
|
||||
#include <art/paperback/page.hxx>
|
||||
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
#include <art/paperback/carousel/stream.hxx>
|
||||
|
||||
#include <art/paperback/internals/cp1252.hxx>
|
||||
#include <art/paperback/internals/graphics-state.hxx>
|
||||
#include <art/paperback/internals/resource-collection.hxx>
|
||||
|
||||
namespace Art::Paperback::Graphics
|
||||
{
|
||||
|
||||
/// Holds internal canvas data.
|
||||
///
|
||||
struct Canvas::Internal
|
||||
{
|
||||
Internal(Page& page)
|
||||
: page{page},
|
||||
contents{page.contents()}
|
||||
{
|
||||
gstates.push(Internals::Graphics_state{});
|
||||
}
|
||||
|
||||
/// Write single byte to content stream.
|
||||
///
|
||||
void
|
||||
write(char c)
|
||||
{
|
||||
contents->write(&c, 1);
|
||||
}
|
||||
|
||||
/// Write string to content stream.
|
||||
///
|
||||
void
|
||||
write(string const& str)
|
||||
{
|
||||
contents->write(str.c_str(), str.size());
|
||||
}
|
||||
|
||||
/// Write real value to stream.
|
||||
///
|
||||
void
|
||||
write(double v)
|
||||
{
|
||||
stringstream str;
|
||||
str.imbue(std::locale::classic());
|
||||
str << std::fixed << v;
|
||||
write(str.str());
|
||||
}
|
||||
|
||||
/// Write integer value to stream.
|
||||
///
|
||||
void
|
||||
write(long long int v)
|
||||
{
|
||||
stringstream str;
|
||||
str.imbue(std::locale::classic());
|
||||
str << v;
|
||||
write(str.str());
|
||||
}
|
||||
|
||||
/// Write name to content stream.
|
||||
///
|
||||
void
|
||||
write_name(string const& n)
|
||||
{
|
||||
write('/');
|
||||
write(n);
|
||||
}
|
||||
|
||||
/// Write encoded text string to content stream.
|
||||
///
|
||||
/// The text is hexadecimally coded.
|
||||
///
|
||||
void
|
||||
write_encode_text(string const& text)
|
||||
{
|
||||
auto to_hex = [](char c) -> char
|
||||
{
|
||||
return (c < 10) ? ('0' + c) : ('a' + (c - 10));
|
||||
};
|
||||
|
||||
auto encoded = Internals::cp1252::from_utf8(text);
|
||||
|
||||
write('<');
|
||||
for (auto const& j : encoded) {
|
||||
char high = (j & 0xF0) >> 4;
|
||||
char low = (j & 0x0F);
|
||||
|
||||
write(to_hex(high));
|
||||
write(to_hex(low));
|
||||
}
|
||||
write('>');
|
||||
}
|
||||
|
||||
/// Reference to parent page.
|
||||
///
|
||||
Page& page;
|
||||
|
||||
/// Canvas content stream.
|
||||
///
|
||||
Carousel::Stream& contents;
|
||||
|
||||
/// Holds the graphics states.
|
||||
///
|
||||
stack<Internals::Graphics_state> gstates;
|
||||
|
||||
};
|
||||
|
||||
/// \param page The parent page of this canvas.
|
||||
///
|
||||
Canvas::
|
||||
Canvas(Clear const&, Page& page)
|
||||
: internal{new Internal{page}}
|
||||
{
|
||||
content().clear();
|
||||
}
|
||||
|
||||
Canvas::
|
||||
~Canvas()
|
||||
{}
|
||||
|
||||
Page&
|
||||
Canvas::
|
||||
page()
|
||||
{
|
||||
return internal->page;
|
||||
}
|
||||
|
||||
Page const&
|
||||
Canvas::
|
||||
page() const
|
||||
{
|
||||
return internal->page;
|
||||
}
|
||||
|
||||
Carousel::Stream&
|
||||
Canvas::
|
||||
content()
|
||||
{
|
||||
return internal->contents;
|
||||
}
|
||||
|
||||
Carousel::Stream const&
|
||||
Canvas::
|
||||
content() const
|
||||
{
|
||||
return internal->contents;
|
||||
}
|
||||
|
||||
/// \param grey_level The new grey level.
|
||||
///
|
||||
void
|
||||
Canvas::
|
||||
set_stroke(double grey_level)
|
||||
{
|
||||
if (grey_level < 0 || 1 < grey_level) {
|
||||
throw std::out_of_range{"grey level out of range (0-1)"};
|
||||
}
|
||||
|
||||
internal->write(grey_level);
|
||||
internal->write("G\r\n");
|
||||
|
||||
internal->gstates.top().cs_stroke = Color_space::device_grey;
|
||||
internal->gstates.top().grey_stroke = grey_level;
|
||||
}
|
||||
|
||||
/// \param grey_level The new grey level.
|
||||
///
|
||||
void
|
||||
Canvas::
|
||||
set_fill(double grey_level)
|
||||
{
|
||||
if (grey_level < 0 || 1 < grey_level) {
|
||||
throw std::out_of_range{"grey level out of range (0-1)"};
|
||||
}
|
||||
|
||||
internal->write(grey_level);
|
||||
internal->write(' ');
|
||||
internal->write("g\r\n");
|
||||
|
||||
internal->gstates.top().cs_fill = Color_space::device_grey;
|
||||
internal->gstates.top().grey_fill = grey_level;
|
||||
}
|
||||
|
||||
/// \param color The new RGB color.
|
||||
///
|
||||
void
|
||||
Canvas::
|
||||
set_stroke(RGB const& color)
|
||||
{
|
||||
internal->write(color.red());
|
||||
internal->write(' ');
|
||||
internal->write(color.green());
|
||||
internal->write(' ');
|
||||
internal->write(color.blue());
|
||||
internal->write(' ');
|
||||
internal->write("RG\r\n");
|
||||
|
||||
internal->gstates.top().cs_stroke = Color_space::device_rgb;
|
||||
internal->gstates.top().rgb_stroke = color;
|
||||
}
|
||||
|
||||
/// \param color The new RGB color.
|
||||
///
|
||||
void
|
||||
Canvas::
|
||||
set_fill(RGB const& color)
|
||||
{
|
||||
internal->write(color.red());
|
||||
internal->write(' ');
|
||||
internal->write(color.green());
|
||||
internal->write(' ');
|
||||
internal->write(color.blue());
|
||||
internal->write(' ');
|
||||
internal->write("rg\r\n");
|
||||
|
||||
internal->gstates.top().cs_fill = Color_space::device_rgb;
|
||||
internal->gstates.top().rgb_fill = color;
|
||||
}
|
||||
|
||||
/// \param text The text for which to compute the width.
|
||||
///
|
||||
double
|
||||
Canvas::
|
||||
get_text_width(string const& text) const
|
||||
{
|
||||
auto& gstate = internal->gstates.top();
|
||||
auto& font = *gstate.current_font;
|
||||
auto font_size = gstate.font_size;
|
||||
|
||||
auto tw = font.get_text_width(text);
|
||||
|
||||
auto width = static_cast<double>(tw.width) * font_size / 1000;
|
||||
|
||||
// FIXME: the missing functions mentioned below.
|
||||
//
|
||||
width += tw.character_count * 0; // gstate.get_text_character_spacing();
|
||||
width += tw.space_count * 0; // gstate.get_text_word_spacing();
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
/// \param canvas The parent canvas.
|
||||
///
|
||||
Canvas::Save::
|
||||
Save(Canvas& canvas)
|
||||
: _canvas{canvas}
|
||||
{
|
||||
canvas.internal->gstates.push(canvas.internal->gstates.top());
|
||||
}
|
||||
|
||||
Canvas::Save::
|
||||
~Save() noexcept
|
||||
{
|
||||
canvas().internal->gstates.pop();
|
||||
}
|
||||
|
||||
Canvas&
|
||||
Canvas::Save::
|
||||
canvas()
|
||||
{
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
Canvas const&
|
||||
Canvas::Save::
|
||||
canvas() const
|
||||
{
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
/// \param canvas The parent canvas.
|
||||
/// \param mode The paint mode.
|
||||
///
|
||||
Canvas::Path::
|
||||
Path(Canvas& canvas, Paint_mode mode)
|
||||
: _canvas{canvas},
|
||||
_mode{mode}
|
||||
{}
|
||||
|
||||
Canvas::Path::
|
||||
~Path() noexcept
|
||||
{
|
||||
canvas().internal->write("h\r\n");
|
||||
|
||||
switch (_mode) {
|
||||
case stroke:
|
||||
canvas().internal->write("S\r\n");
|
||||
break;
|
||||
|
||||
case fill:
|
||||
canvas().internal->write("R\r\n");
|
||||
break;
|
||||
|
||||
case fill_then_stroke:
|
||||
canvas().internal->write("B\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// \return Returns a reference to the parent canvas.
|
||||
///
|
||||
Canvas&
|
||||
Canvas::Path::
|
||||
canvas()
|
||||
{
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
/// \return Returns a reference to the parent canvas.
|
||||
///
|
||||
Canvas const&
|
||||
Canvas::Path::
|
||||
canvas() const
|
||||
{
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
void
|
||||
Canvas::Path::
|
||||
move_to(double x, double y)
|
||||
{
|
||||
canvas().internal->write(x);
|
||||
canvas().internal->write(' ');
|
||||
canvas().internal->write(y);
|
||||
canvas().internal->write(" m\r\n");
|
||||
}
|
||||
|
||||
void
|
||||
Canvas::Path::
|
||||
line_to(double x, double y)
|
||||
{
|
||||
canvas().internal->write(x);
|
||||
canvas().internal->write(' ');
|
||||
canvas().internal->write(y);
|
||||
canvas().internal->write(" l\r\n");
|
||||
}
|
||||
|
||||
void
|
||||
Canvas::Path::
|
||||
bezier_curve_to(double x1,
|
||||
double y1,
|
||||
double x2,
|
||||
double y2,
|
||||
double x3,
|
||||
double y3)
|
||||
{
|
||||
canvas().internal->write(x1);
|
||||
canvas().internal->write(' ');
|
||||
canvas().internal->write(y1);
|
||||
canvas().internal->write(' ');
|
||||
canvas().internal->write(x2);
|
||||
canvas().internal->write(' ');
|
||||
canvas().internal->write(y2);
|
||||
canvas().internal->write(' ');
|
||||
canvas().internal->write(x3);
|
||||
canvas().internal->write(' ');
|
||||
canvas().internal->write(y3);
|
||||
canvas().internal->write(" c\r\n");
|
||||
}
|
||||
|
||||
/// \param canvas The parent canvas.
|
||||
///
|
||||
Canvas::Begin_text::
|
||||
Begin_text(Canvas& canvas)
|
||||
: _canvas{canvas}
|
||||
{
|
||||
canvas.internal->write("BT\r\n");
|
||||
}
|
||||
|
||||
Canvas::Begin_text::
|
||||
~Begin_text() noexcept
|
||||
{
|
||||
canvas().internal->write("ET\r\n");
|
||||
}
|
||||
|
||||
Canvas&
|
||||
Canvas::Begin_text::
|
||||
canvas()
|
||||
{
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
Canvas const&
|
||||
Canvas::Begin_text::
|
||||
canvas() const
|
||||
{
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
void
|
||||
Canvas::Begin_text::
|
||||
move_text_pos(double x, double y)
|
||||
{
|
||||
canvas().internal->write(x);
|
||||
canvas().internal->write(' ');
|
||||
canvas().internal->write(y);
|
||||
canvas().internal->write(" Td\r\n");
|
||||
}
|
||||
|
||||
void
|
||||
Canvas::Begin_text::
|
||||
show_text(string const& text)
|
||||
{
|
||||
canvas().internal->write_encode_text(text);
|
||||
canvas().internal->write(" Tj\r\n");
|
||||
}
|
||||
|
||||
/// \param canvas The parent canvas.
|
||||
/// \param f The new font.
|
||||
/// \param size The font size.
|
||||
/// \throw Invalid_operation Thrown if a font is already set.
|
||||
///
|
||||
Canvas::Set_font::
|
||||
Set_font(Canvas& canvas, Font& f, double size)
|
||||
: _canvas{canvas}
|
||||
{
|
||||
if (canvas.internal->gstates.top().current_font) {
|
||||
|
||||
raise<Invalid_operation>{} << "font already set";
|
||||
}
|
||||
|
||||
auto local_name = canvas.page().resources().fonts().embed(f);
|
||||
|
||||
canvas.internal->write_name(*local_name);
|
||||
canvas.internal->write(' ');
|
||||
canvas.internal->write(size);
|
||||
canvas.internal->write(" Tf ");
|
||||
|
||||
canvas.internal->gstates.top().current_font = &f;
|
||||
canvas.internal->gstates.top().font_size = size;
|
||||
}
|
||||
|
||||
/// The current font will be resent during deconstruction.
|
||||
///
|
||||
Canvas::Set_font::
|
||||
~Set_font() noexcept
|
||||
{
|
||||
canvas().internal->gstates.top().current_font = nullptr;
|
||||
}
|
||||
|
||||
Canvas&
|
||||
Canvas::Set_font::
|
||||
canvas()
|
||||
{
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
Canvas const&
|
||||
Canvas::Set_font::
|
||||
canvas() const
|
||||
{
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Graphics
|
||||
274
art/paperback/graphics/canvas.hxx
Normal file
274
art/paperback/graphics/canvas.hxx
Normal file
@@ -0,0 +1,274 @@
|
||||
#ifndef art__paperback__graphics__canvas_hxx_
|
||||
#define art__paperback__graphics__canvas_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/graphics/color.hxx>
|
||||
|
||||
namespace Art::Paperback::Graphics
|
||||
{
|
||||
|
||||
/// Represents the drawable canvas of a page.
|
||||
///
|
||||
class Canvas
|
||||
{
|
||||
public:
|
||||
struct Clear {};
|
||||
|
||||
/// Dispatch-tag used to clear a canvas.
|
||||
///
|
||||
static constexpr Clear const clear{};
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Canvas(Clear const&, Page&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Canvas();
|
||||
|
||||
/// Access the parent page.
|
||||
///
|
||||
Page&
|
||||
page();
|
||||
|
||||
/// Access the parent page.
|
||||
///
|
||||
Page const&
|
||||
page() const;
|
||||
|
||||
/// Access the content stream.
|
||||
///
|
||||
Carousel::Stream&
|
||||
content();
|
||||
|
||||
/// Access the content stream.
|
||||
///
|
||||
Carousel::Stream const&
|
||||
content() const;
|
||||
|
||||
class Save;
|
||||
friend Save;
|
||||
|
||||
/// Set stroke greyscale value.
|
||||
///
|
||||
void
|
||||
set_stroke(double);
|
||||
|
||||
/// Set fill greyscale value.
|
||||
///
|
||||
void
|
||||
set_fill(double);
|
||||
|
||||
/// Set stroke RGB color.
|
||||
///
|
||||
void
|
||||
set_stroke(RGB const&);
|
||||
|
||||
/// Set fill RGB color.
|
||||
///
|
||||
void
|
||||
set_fill(RGB const&);
|
||||
|
||||
class Path;
|
||||
friend Path;
|
||||
|
||||
class Begin_text;
|
||||
friend Begin_text;
|
||||
|
||||
class Set_font;
|
||||
friend Set_font;
|
||||
|
||||
/// Compute text width.
|
||||
///
|
||||
double
|
||||
get_text_width(string const&) const;
|
||||
|
||||
private:
|
||||
Canvas(Canvas const&) = delete;
|
||||
Canvas(Canvas&&) = delete;
|
||||
Canvas& operator=(Canvas const&) = delete;
|
||||
Canvas& operator=(Canvas&&) = delete;
|
||||
|
||||
private:
|
||||
struct Internal;
|
||||
unique_ptr<Internal> internal;
|
||||
|
||||
};
|
||||
|
||||
/// Saves graphics state.
|
||||
///
|
||||
class Canvas::Save
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
explicit
|
||||
Save(Canvas&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Save() noexcept;
|
||||
|
||||
/// Access parent canvas.
|
||||
///
|
||||
Canvas&
|
||||
canvas();
|
||||
|
||||
/// Access parent canvas.
|
||||
Canvas const&
|
||||
canvas() const;
|
||||
|
||||
private:
|
||||
Save(Save const&) = delete;
|
||||
Save(Save&&) = delete;
|
||||
Save& operator=(Save const&) = delete;
|
||||
Save& operator=(Save&&) = delete;
|
||||
|
||||
private:
|
||||
Canvas& _canvas;
|
||||
|
||||
};
|
||||
|
||||
/// Path construction class.
|
||||
///
|
||||
/// \todo Add example.
|
||||
///
|
||||
class Canvas::Path
|
||||
{
|
||||
public:
|
||||
/// Paint mode enumeration.
|
||||
///
|
||||
enum Paint_mode
|
||||
{
|
||||
/// Stroke.
|
||||
///
|
||||
stroke,
|
||||
|
||||
/// Fill.
|
||||
///
|
||||
fill,
|
||||
|
||||
/// Fill then stroke.
|
||||
///
|
||||
fill_then_stroke
|
||||
|
||||
};
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Path(Canvas&, Paint_mode);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Path() noexcept;
|
||||
|
||||
/// Access parent canvas.
|
||||
///
|
||||
Canvas&
|
||||
canvas();
|
||||
|
||||
/// Access parent canvas.
|
||||
Canvas const&
|
||||
canvas() const;
|
||||
|
||||
/// Move starting point.
|
||||
///
|
||||
void
|
||||
move_to(double, double);
|
||||
|
||||
/// Draw line.
|
||||
///
|
||||
void
|
||||
line_to(double, double);
|
||||
|
||||
/// Draw bezier curve.
|
||||
///
|
||||
void
|
||||
bezier_curve_to(double,
|
||||
double,
|
||||
double,
|
||||
double,
|
||||
double,
|
||||
double);
|
||||
|
||||
|
||||
private:
|
||||
Path(Path const&) = delete;
|
||||
Path(Path&&) = delete;
|
||||
Path& operator=(Path const&) = delete;
|
||||
Path& operator=(Path&&) = delete;
|
||||
|
||||
private:
|
||||
Canvas& _canvas;
|
||||
Paint_mode _mode;
|
||||
|
||||
};
|
||||
|
||||
class Canvas::Begin_text
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
Begin_text(Canvas&);
|
||||
|
||||
Begin_text(Begin_text const&) = delete;
|
||||
|
||||
Begin_text(Begin_text&&) = delete;
|
||||
|
||||
~Begin_text() noexcept;
|
||||
|
||||
Canvas&
|
||||
canvas();
|
||||
|
||||
Canvas const&
|
||||
canvas() const;
|
||||
|
||||
void
|
||||
move_text_pos(double, double);
|
||||
|
||||
void
|
||||
show_text(string const&);
|
||||
|
||||
Begin_text&
|
||||
operator=(Begin_text const&) = delete;
|
||||
|
||||
Begin_text&
|
||||
operator=(Begin_text&&) = delete;
|
||||
|
||||
private:
|
||||
Canvas& _canvas;
|
||||
|
||||
};
|
||||
|
||||
class Canvas::Set_font
|
||||
{
|
||||
public:
|
||||
Set_font(Canvas&, Font&, double);
|
||||
|
||||
Set_font(Set_font const&) = delete;
|
||||
|
||||
Set_font(Set_font&&) = delete;
|
||||
|
||||
~Set_font() noexcept;
|
||||
|
||||
Canvas&
|
||||
canvas();
|
||||
|
||||
Canvas const&
|
||||
canvas() const;
|
||||
|
||||
Set_font&
|
||||
operator=(Set_font const&) = delete;
|
||||
|
||||
Set_font&
|
||||
operator=(Set_font&&) = delete;
|
||||
|
||||
private:
|
||||
Canvas& _canvas;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Graphics
|
||||
|
||||
#endif
|
||||
91
art/paperback/graphics/color.cxx
Normal file
91
art/paperback/graphics/color.cxx
Normal file
@@ -0,0 +1,91 @@
|
||||
#include <art/paperback/graphics/color.hxx>
|
||||
|
||||
namespace Art::Paperback::Graphics
|
||||
{
|
||||
|
||||
RGB::
|
||||
RGB()
|
||||
{}
|
||||
|
||||
/// \param red The red component.
|
||||
/// \param green The green component.
|
||||
/// \param blue The blue component.
|
||||
RGB::
|
||||
RGB(double red, double green, double blue)
|
||||
: _red{red},
|
||||
_green{green},
|
||||
_blue{blue}
|
||||
{}
|
||||
|
||||
/// \param other The RGB to copy from.
|
||||
///
|
||||
RGB::
|
||||
RGB(RGB const& other)
|
||||
: _red{other._red},
|
||||
_green{other._green},
|
||||
_blue{other._blue}
|
||||
{}
|
||||
|
||||
/// \param other The RGB to move from.
|
||||
///
|
||||
RGB::
|
||||
RGB(RGB&& other)
|
||||
: _red{other._red},
|
||||
_green{other._green},
|
||||
_blue{other._blue}
|
||||
{}
|
||||
|
||||
/// \return Returns the red component.
|
||||
///
|
||||
double
|
||||
RGB::
|
||||
red() const
|
||||
{
|
||||
return _red;
|
||||
}
|
||||
|
||||
/// \return Returns the green component.
|
||||
///
|
||||
double
|
||||
RGB::
|
||||
green() const
|
||||
{
|
||||
return _green;
|
||||
}
|
||||
|
||||
/// \return Returns the blue component.
|
||||
///
|
||||
double
|
||||
RGB::
|
||||
blue() const
|
||||
{
|
||||
return _blue;
|
||||
}
|
||||
|
||||
/// \param other The RGB to copy from.
|
||||
///
|
||||
RGB&
|
||||
RGB::
|
||||
operator=(RGB const& other)
|
||||
{
|
||||
_red = other._red;
|
||||
_green = other._green;
|
||||
_blue = other._blue;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \param other The RGB to move from.
|
||||
///
|
||||
RGB&
|
||||
RGB::
|
||||
operator=(RGB&& other)
|
||||
{
|
||||
_red = other._red;
|
||||
_green = other._green;
|
||||
_blue = other._blue;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Graphics
|
||||
78
art/paperback/graphics/color.hxx
Normal file
78
art/paperback/graphics/color.hxx
Normal file
@@ -0,0 +1,78 @@
|
||||
#ifndef art__paperback__graphics__color_hxx_
|
||||
#define art__paperback__graphics__color_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
|
||||
namespace Art::Paperback::Graphics
|
||||
{
|
||||
|
||||
/// PDF color space enumeration.
|
||||
///
|
||||
enum class Color_space
|
||||
{
|
||||
/// Device grey.
|
||||
///
|
||||
device_grey,
|
||||
|
||||
/// Device RGB.
|
||||
///
|
||||
device_rgb
|
||||
|
||||
};
|
||||
|
||||
/// Represents an RGB color value.
|
||||
///
|
||||
class RGB
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
RGB();
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
RGB(RGB const&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
RGB(RGB&&);
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
RGB(double, double, double);
|
||||
|
||||
/// Access red component.
|
||||
///
|
||||
double
|
||||
red() const;
|
||||
|
||||
/// Access green component.
|
||||
///
|
||||
double
|
||||
green() const;
|
||||
|
||||
/// Access blue component.
|
||||
///
|
||||
double
|
||||
blue() const;
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
RGB&
|
||||
operator=(RGB const&);
|
||||
|
||||
/// Assignment.
|
||||
///
|
||||
RGB&
|
||||
operator=(RGB&&);
|
||||
|
||||
private:
|
||||
double _red{};
|
||||
double _green{};
|
||||
double _blue{};
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Graphics
|
||||
|
||||
#endif
|
||||
107
art/paperback/graphics/font.hxx
Normal file
107
art/paperback/graphics/font.hxx
Normal file
@@ -0,0 +1,107 @@
|
||||
#ifndef art__paperback__graphics__font_hxx_
|
||||
#define art__paperback__graphics__font_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
namespace Art::Paperback::Graphics
|
||||
{
|
||||
|
||||
/// Represents text width computations.
|
||||
///
|
||||
struct Text_width
|
||||
{
|
||||
/// Character count.
|
||||
///
|
||||
uint32_t character_count{};
|
||||
|
||||
/// Space count.
|
||||
///
|
||||
uint32_t space_count{};
|
||||
|
||||
/// Text width.
|
||||
///
|
||||
double width{};
|
||||
|
||||
};
|
||||
|
||||
/// Base class for fonts.
|
||||
///
|
||||
class Font
|
||||
{
|
||||
public:
|
||||
/// Destructor.
|
||||
///
|
||||
virtual
|
||||
~Font() noexcept
|
||||
{}
|
||||
|
||||
/// Access the parent document of this font.
|
||||
///
|
||||
virtual
|
||||
Document&
|
||||
document() = 0;
|
||||
|
||||
/// Get the COS object for this font.
|
||||
///
|
||||
virtual
|
||||
Carousel::Object
|
||||
object() = 0;
|
||||
|
||||
/// Get the name of the font.
|
||||
///
|
||||
/// \return Returns the name of the font.
|
||||
///
|
||||
virtual
|
||||
string
|
||||
name() const = 0;
|
||||
|
||||
/// Get the font ascent.
|
||||
///
|
||||
/// \return Returns the font ascent.
|
||||
///
|
||||
virtual
|
||||
int16_t
|
||||
get_ascent() const = 0;
|
||||
|
||||
/// Get the font descent.
|
||||
///
|
||||
virtual
|
||||
int16_t
|
||||
get_descent() const = 0;
|
||||
|
||||
/// Get the font X-height.
|
||||
///
|
||||
virtual
|
||||
uint16_t
|
||||
get_xheight() const = 0;
|
||||
|
||||
/// Get the font cap-height.
|
||||
///
|
||||
virtual
|
||||
uint16_t
|
||||
get_capheight() const = 0;
|
||||
|
||||
/// Compute text width.
|
||||
///
|
||||
virtual
|
||||
Text_width
|
||||
get_text_width(string const&) = 0;
|
||||
|
||||
protected:
|
||||
/// Constructor.
|
||||
///
|
||||
Font()
|
||||
{}
|
||||
|
||||
private:
|
||||
Font(Font const&) = delete;
|
||||
Font(Font&&) = delete;
|
||||
Font& operator=(Font const&) = delete;
|
||||
Font& operator=(Font&&) = delete;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Graphics
|
||||
|
||||
#endif
|
||||
158
art/paperback/graphics/standard-font.cxx
Normal file
158
art/paperback/graphics/standard-font.cxx
Normal file
@@ -0,0 +1,158 @@
|
||||
#include <art/paperback/graphics/standard-font.hxx>
|
||||
|
||||
#include <art/paperback/document.hxx>
|
||||
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
#include <art/paperback/carousel/file.hxx>
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
|
||||
#include <art/paperback/internals/base14-fontdata.hxx>
|
||||
|
||||
#include <art/unicode/reader.hxx>
|
||||
#include <art/unicode/utf8-decoder.hxx>
|
||||
|
||||
namespace Art::Paperback::Graphics
|
||||
{
|
||||
|
||||
struct Standard_font::Internal
|
||||
{
|
||||
Internal(Document& document)
|
||||
: document{document},
|
||||
object{document.file().create_object<Carousel::Dictionary>()},
|
||||
data{object_cast<Carousel::Dictionary>(object)}
|
||||
{}
|
||||
|
||||
Document& document;
|
||||
Carousel::Object object;
|
||||
Carousel::Dictionary& data;
|
||||
|
||||
string name;
|
||||
|
||||
Internals::Base14_font_data const* font_data;
|
||||
|
||||
};
|
||||
|
||||
/// \class Standard_font
|
||||
///
|
||||
/// This class represents one of the 14 standard fonts supported by
|
||||
/// the PDF-specification.
|
||||
///
|
||||
|
||||
/// \param document A reference to the parent document.
|
||||
/// \param base_font The name of the base font.
|
||||
///
|
||||
Standard_font::
|
||||
Standard_font(Document& document, string const& base_font)
|
||||
: internal{new Internal{document}}
|
||||
{
|
||||
internal->data.insert("Type", Carousel::Name{"Font"});
|
||||
internal->data.insert("Subtype", Carousel::Name{"Type1"});
|
||||
internal->data.insert("BaseFont", Carousel::Name{base_font});
|
||||
internal->data.insert("Encoding", Carousel::Name{"WinAnsiEncoding"});
|
||||
|
||||
internal->name = base_font;
|
||||
|
||||
auto font_data = Internals::base14_fonts.find(base_font);
|
||||
|
||||
if (font_data == Internals::base14_fonts.end()) {
|
||||
throw std::invalid_argument{"invalid base font"};
|
||||
}
|
||||
|
||||
internal->font_data = &font_data->second;
|
||||
}
|
||||
|
||||
Standard_font::
|
||||
~Standard_font() noexcept
|
||||
{}
|
||||
|
||||
Document&
|
||||
Standard_font::
|
||||
document()
|
||||
{
|
||||
return internal->document;
|
||||
}
|
||||
|
||||
Carousel::Object
|
||||
Standard_font::
|
||||
object()
|
||||
{
|
||||
return internal->object;
|
||||
}
|
||||
|
||||
string
|
||||
Standard_font::
|
||||
name() const
|
||||
{
|
||||
return internal->name;
|
||||
}
|
||||
|
||||
/// \return Returns the font ascent.
|
||||
///
|
||||
int16_t
|
||||
Standard_font::
|
||||
get_ascent() const
|
||||
{
|
||||
return internal->font_data->ascent;
|
||||
}
|
||||
|
||||
/// \return Returns the font descent.
|
||||
///
|
||||
int16_t
|
||||
Standard_font::
|
||||
get_descent() const
|
||||
{
|
||||
return internal->font_data->descent;
|
||||
}
|
||||
|
||||
/// \return Returns the font X-height.
|
||||
///
|
||||
uint16_t
|
||||
Standard_font::
|
||||
get_xheight() const
|
||||
{
|
||||
return internal->font_data->x_height;
|
||||
}
|
||||
|
||||
/// \return Returns the font Cap-height.
|
||||
///
|
||||
uint16_t
|
||||
Standard_font::
|
||||
get_capheight() const
|
||||
{
|
||||
return internal->font_data->cap_height;
|
||||
}
|
||||
|
||||
/// \a text is assumed to be encoded in UTF-8 and will be
|
||||
/// decoded as such during text width computation.
|
||||
///
|
||||
/// \param text The text for which to compute metrics.
|
||||
///
|
||||
Text_width
|
||||
Standard_font::
|
||||
get_text_width(string const& text)
|
||||
{
|
||||
Text_width tw{};
|
||||
|
||||
art::unicode::iterator_reader_t reader{text.begin(), text.end()};
|
||||
art::unicode::utf8_decoder_t decoder{reader};
|
||||
|
||||
for (auto it = decoder.begin(); it != decoder.end(); ++it) {
|
||||
auto const c = *it;
|
||||
|
||||
if (c == ' ')
|
||||
++tw.space_count;
|
||||
|
||||
++tw.character_count;
|
||||
|
||||
for (auto const& j : internal->font_data->cdata) {
|
||||
if (c == j.unicode) {
|
||||
tw.width += j.width;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tw;
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Graphics
|
||||
64
art/paperback/graphics/standard-font.hxx
Normal file
64
art/paperback/graphics/standard-font.hxx
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef art__paperback__graphics__standard_font_hxx_
|
||||
#define art__paperback__graphics__standard_font_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/graphics/font.hxx>
|
||||
|
||||
namespace Art::Paperback::Graphics
|
||||
{
|
||||
|
||||
/// Represents a standard PDF font.
|
||||
///
|
||||
class Standard_font
|
||||
: public Font
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
Standard_font(Document&, string const&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Standard_font() noexcept override;
|
||||
|
||||
Document&
|
||||
document() override;
|
||||
|
||||
Carousel::Object
|
||||
object() override;
|
||||
|
||||
string
|
||||
name() const override;
|
||||
|
||||
int16_t
|
||||
get_ascent() const override;
|
||||
|
||||
int16_t
|
||||
get_descent() const override;
|
||||
|
||||
uint16_t
|
||||
get_xheight() const override;
|
||||
|
||||
uint16_t
|
||||
get_capheight() const override;
|
||||
|
||||
Text_width
|
||||
get_text_width(string const&) override;
|
||||
|
||||
private:
|
||||
Standard_font(Standard_font const&) = delete;
|
||||
Standard_font(Standard_font&&) = delete;
|
||||
Standard_font& operator=(Standard_font const&) = delete;
|
||||
Standard_font& operator=(Standard_font&&) = delete;
|
||||
|
||||
private:
|
||||
struct Internal;
|
||||
unique_ptr<Internal> internal;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Graphics
|
||||
|
||||
#endif
|
||||
1558
art/paperback/internals/base14-fontdata.cxx
Normal file
1558
art/paperback/internals/base14-fontdata.cxx
Normal file
File diff suppressed because it is too large
Load Diff
64
art/paperback/internals/base14-fontdata.hxx
Normal file
64
art/paperback/internals/base14-fontdata.hxx
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef art__paperback__internals__base14_fontdata_hxx_
|
||||
#define art__paperback__internals__base14_fontdata_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/primitives.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals
|
||||
{
|
||||
|
||||
/// Base font character data.
|
||||
///
|
||||
struct Base14_character_data
|
||||
{
|
||||
int16_t char_cd;
|
||||
|
||||
/// Unicode code point.
|
||||
///
|
||||
uint16_t unicode;
|
||||
|
||||
double width;
|
||||
|
||||
};
|
||||
|
||||
/// Represents font data for one of the 14 standard PDF fonts.
|
||||
///
|
||||
struct Base14_font_data
|
||||
{
|
||||
/// Font character data.
|
||||
///
|
||||
vector<Base14_character_data> const& cdata;
|
||||
|
||||
bool is_font_specific;
|
||||
|
||||
/// Font ascent.
|
||||
///
|
||||
int16_t ascent;
|
||||
|
||||
/// Font descent.
|
||||
///
|
||||
int16_t descent;
|
||||
|
||||
/// Font X-height.
|
||||
///
|
||||
uint16_t x_height;
|
||||
|
||||
/// Font Cap-height.
|
||||
///
|
||||
uint16_t cap_height;
|
||||
|
||||
/// Font bounding box.
|
||||
///
|
||||
Rectangle bbox;
|
||||
|
||||
};
|
||||
|
||||
/// Map of base fonts.
|
||||
///
|
||||
extern
|
||||
map<string, Base14_font_data> const
|
||||
base14_fonts;
|
||||
|
||||
} // namespace Art::Paperback::Internals
|
||||
|
||||
#endif
|
||||
533
art/paperback/internals/cp1252.cxx
Normal file
533
art/paperback/internals/cp1252.cxx
Normal file
@@ -0,0 +1,533 @@
|
||||
#include <art/paperback/internals/cp1252.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals::cp1252
|
||||
{
|
||||
|
||||
char
|
||||
from_unicode(uint32_t unicode)
|
||||
{
|
||||
switch (unicode) {
|
||||
case 0x0000:
|
||||
return static_cast<char>(0x00);
|
||||
case 0x0001:
|
||||
return static_cast<char>(0x01);
|
||||
case 0x0002:
|
||||
return static_cast<char>(0x02);
|
||||
case 0x0003:
|
||||
return static_cast<char>(0x03);
|
||||
case 0x0004:
|
||||
return static_cast<char>(0x04);
|
||||
case 0x0005:
|
||||
return static_cast<char>(0x05);
|
||||
case 0x0006:
|
||||
return static_cast<char>(0x06);
|
||||
case 0x0007:
|
||||
return static_cast<char>(0x07);
|
||||
case 0x0008:
|
||||
return static_cast<char>(0x08);
|
||||
case 0x0009:
|
||||
return static_cast<char>(0x09);
|
||||
case 0x000a:
|
||||
return static_cast<char>(0x0a);
|
||||
case 0x000b:
|
||||
return static_cast<char>(0x0b);
|
||||
case 0x000c:
|
||||
return static_cast<char>(0x0c);
|
||||
case 0x000d:
|
||||
return static_cast<char>(0x0d);
|
||||
case 0x000e:
|
||||
return static_cast<char>(0x0e);
|
||||
case 0x000f:
|
||||
return static_cast<char>(0x0f);
|
||||
case 0x0010:
|
||||
return static_cast<char>(0x10);
|
||||
case 0x0011:
|
||||
return static_cast<char>(0x11);
|
||||
case 0x0012:
|
||||
return static_cast<char>(0x12);
|
||||
case 0x0013:
|
||||
return static_cast<char>(0x13);
|
||||
case 0x0014:
|
||||
return static_cast<char>(0x14);
|
||||
case 0x0015:
|
||||
return static_cast<char>(0x15);
|
||||
case 0x0016:
|
||||
return static_cast<char>(0x16);
|
||||
case 0x0017:
|
||||
return static_cast<char>(0x17);
|
||||
case 0x0018:
|
||||
return static_cast<char>(0x18);
|
||||
case 0x0019:
|
||||
return static_cast<char>(0x19);
|
||||
case 0x001a:
|
||||
return static_cast<char>(0x1a);
|
||||
case 0x001b:
|
||||
return static_cast<char>(0x1b);
|
||||
case 0x001c:
|
||||
return static_cast<char>(0x1c);
|
||||
case 0x001d:
|
||||
return static_cast<char>(0x1d);
|
||||
case 0x001e:
|
||||
return static_cast<char>(0x1e);
|
||||
case 0x001f:
|
||||
return static_cast<char>(0x1f);
|
||||
case 0x0020:
|
||||
return static_cast<char>(0x20);
|
||||
case 0x0021:
|
||||
return static_cast<char>(0x21);
|
||||
case 0x0022:
|
||||
return static_cast<char>(0x22);
|
||||
case 0x0023:
|
||||
return static_cast<char>(0x23);
|
||||
case 0x0024:
|
||||
return static_cast<char>(0x24);
|
||||
case 0x0025:
|
||||
return static_cast<char>(0x25);
|
||||
case 0x0026:
|
||||
return static_cast<char>(0x26);
|
||||
case 0x0027:
|
||||
return static_cast<char>(0x27);
|
||||
case 0x0028:
|
||||
return static_cast<char>(0x28);
|
||||
case 0x0029:
|
||||
return static_cast<char>(0x29);
|
||||
case 0x002a:
|
||||
return static_cast<char>(0x2a);
|
||||
case 0x002b:
|
||||
return static_cast<char>(0x2b);
|
||||
case 0x002c:
|
||||
return static_cast<char>(0x2c);
|
||||
case 0x002d:
|
||||
return static_cast<char>(0x2d);
|
||||
case 0x002e:
|
||||
return static_cast<char>(0x2e);
|
||||
case 0x002f:
|
||||
return static_cast<char>(0x2f);
|
||||
case 0x0030:
|
||||
return static_cast<char>(0x30);
|
||||
case 0x0031:
|
||||
return static_cast<char>(0x31);
|
||||
case 0x0032:
|
||||
return static_cast<char>(0x32);
|
||||
case 0x0033:
|
||||
return static_cast<char>(0x33);
|
||||
case 0x0034:
|
||||
return static_cast<char>(0x34);
|
||||
case 0x0035:
|
||||
return static_cast<char>(0x35);
|
||||
case 0x0036:
|
||||
return static_cast<char>(0x36);
|
||||
case 0x0037:
|
||||
return static_cast<char>(0x37);
|
||||
case 0x0038:
|
||||
return static_cast<char>(0x38);
|
||||
case 0x0039:
|
||||
return static_cast<char>(0x39);
|
||||
case 0x003a:
|
||||
return static_cast<char>(0x3a);
|
||||
case 0x003b:
|
||||
return static_cast<char>(0x3b);
|
||||
case 0x003c:
|
||||
return static_cast<char>(0x3c);
|
||||
case 0x003d:
|
||||
return static_cast<char>(0x3d);
|
||||
case 0x003e:
|
||||
return static_cast<char>(0x3e);
|
||||
case 0x003f:
|
||||
return static_cast<char>(0x3f);
|
||||
case 0x0040:
|
||||
return static_cast<char>(0x40);
|
||||
case 0x0041:
|
||||
return static_cast<char>(0x41);
|
||||
case 0x0042:
|
||||
return static_cast<char>(0x42);
|
||||
case 0x0043:
|
||||
return static_cast<char>(0x43);
|
||||
case 0x0044:
|
||||
return static_cast<char>(0x44);
|
||||
case 0x0045:
|
||||
return static_cast<char>(0x45);
|
||||
case 0x0046:
|
||||
return static_cast<char>(0x46);
|
||||
case 0x0047:
|
||||
return static_cast<char>(0x47);
|
||||
case 0x0048:
|
||||
return static_cast<char>(0x48);
|
||||
case 0x0049:
|
||||
return static_cast<char>(0x49);
|
||||
case 0x004a:
|
||||
return static_cast<char>(0x4a);
|
||||
case 0x004b:
|
||||
return static_cast<char>(0x4b);
|
||||
case 0x004c:
|
||||
return static_cast<char>(0x4c);
|
||||
case 0x004d:
|
||||
return static_cast<char>(0x4d);
|
||||
case 0x004e:
|
||||
return static_cast<char>(0x4e);
|
||||
case 0x004f:
|
||||
return static_cast<char>(0x4f);
|
||||
case 0x0050:
|
||||
return static_cast<char>(0x50);
|
||||
case 0x0051:
|
||||
return static_cast<char>(0x51);
|
||||
case 0x0052:
|
||||
return static_cast<char>(0x52);
|
||||
case 0x0053:
|
||||
return static_cast<char>(0x53);
|
||||
case 0x0054:
|
||||
return static_cast<char>(0x54);
|
||||
case 0x0055:
|
||||
return static_cast<char>(0x55);
|
||||
case 0x0056:
|
||||
return static_cast<char>(0x56);
|
||||
case 0x0057:
|
||||
return static_cast<char>(0x57);
|
||||
case 0x0058:
|
||||
return static_cast<char>(0x58);
|
||||
case 0x0059:
|
||||
return static_cast<char>(0x59);
|
||||
case 0x005a:
|
||||
return static_cast<char>(0x5a);
|
||||
case 0x005b:
|
||||
return static_cast<char>(0x5b);
|
||||
case 0x005c:
|
||||
return static_cast<char>(0x5c);
|
||||
case 0x005d:
|
||||
return static_cast<char>(0x5d);
|
||||
case 0x005e:
|
||||
return static_cast<char>(0x5e);
|
||||
case 0x005f:
|
||||
return static_cast<char>(0x5f);
|
||||
case 0x0060:
|
||||
return static_cast<char>(0x60);
|
||||
case 0x0061:
|
||||
return static_cast<char>(0x61);
|
||||
case 0x0062:
|
||||
return static_cast<char>(0x62);
|
||||
case 0x0063:
|
||||
return static_cast<char>(0x63);
|
||||
case 0x0064:
|
||||
return static_cast<char>(0x64);
|
||||
case 0x0065:
|
||||
return static_cast<char>(0x65);
|
||||
case 0x0066:
|
||||
return static_cast<char>(0x66);
|
||||
case 0x0067:
|
||||
return static_cast<char>(0x67);
|
||||
case 0x0068:
|
||||
return static_cast<char>(0x68);
|
||||
case 0x0069:
|
||||
return static_cast<char>(0x69);
|
||||
case 0x006a:
|
||||
return static_cast<char>(0x6a);
|
||||
case 0x006b:
|
||||
return static_cast<char>(0x6b);
|
||||
case 0x006c:
|
||||
return static_cast<char>(0x6c);
|
||||
case 0x006d:
|
||||
return static_cast<char>(0x6d);
|
||||
case 0x006e:
|
||||
return static_cast<char>(0x6e);
|
||||
case 0x006f:
|
||||
return static_cast<char>(0x6f);
|
||||
case 0x0070:
|
||||
return static_cast<char>(0x70);
|
||||
case 0x0071:
|
||||
return static_cast<char>(0x71);
|
||||
case 0x0072:
|
||||
return static_cast<char>(0x72);
|
||||
case 0x0073:
|
||||
return static_cast<char>(0x73);
|
||||
case 0x0074:
|
||||
return static_cast<char>(0x74);
|
||||
case 0x0075:
|
||||
return static_cast<char>(0x75);
|
||||
case 0x0076:
|
||||
return static_cast<char>(0x76);
|
||||
case 0x0077:
|
||||
return static_cast<char>(0x77);
|
||||
case 0x0078:
|
||||
return static_cast<char>(0x78);
|
||||
case 0x0079:
|
||||
return static_cast<char>(0x79);
|
||||
case 0x007a:
|
||||
return static_cast<char>(0x7a);
|
||||
case 0x007b:
|
||||
return static_cast<char>(0x7b);
|
||||
case 0x007c:
|
||||
return static_cast<char>(0x7c);
|
||||
case 0x007d:
|
||||
return static_cast<char>(0x7d);
|
||||
case 0x007e:
|
||||
return static_cast<char>(0x7e);
|
||||
case 0x007f:
|
||||
return static_cast<char>(0x7f);
|
||||
case 0x20ac:
|
||||
return static_cast<char>(0x80);
|
||||
case 0x0081:
|
||||
return static_cast<char>(0x81);
|
||||
case 0x201a:
|
||||
return static_cast<char>(0x82);
|
||||
case 0x0192:
|
||||
return static_cast<char>(0x83);
|
||||
case 0x201e:
|
||||
return static_cast<char>(0x84);
|
||||
case 0x2026:
|
||||
return static_cast<char>(0x85);
|
||||
case 0x2020:
|
||||
return static_cast<char>(0x86);
|
||||
case 0x2021:
|
||||
return static_cast<char>(0x87);
|
||||
case 0x02c6:
|
||||
return static_cast<char>(0x88);
|
||||
case 0x2030:
|
||||
return static_cast<char>(0x89);
|
||||
case 0x0160:
|
||||
return static_cast<char>(0x8a);
|
||||
case 0x2039:
|
||||
return static_cast<char>(0x8b);
|
||||
case 0x0152:
|
||||
return static_cast<char>(0x8c);
|
||||
case 0x008d:
|
||||
return static_cast<char>(0x8d);
|
||||
case 0x017d:
|
||||
return static_cast<char>(0x8e);
|
||||
case 0x008f:
|
||||
return static_cast<char>(0x8f);
|
||||
case 0x0090:
|
||||
return static_cast<char>(0x90);
|
||||
case 0x2018:
|
||||
return static_cast<char>(0x91);
|
||||
case 0x2019:
|
||||
return static_cast<char>(0x92);
|
||||
case 0x201c:
|
||||
return static_cast<char>(0x93);
|
||||
case 0x201d:
|
||||
return static_cast<char>(0x94);
|
||||
case 0x2022:
|
||||
return static_cast<char>(0x95);
|
||||
case 0x2013:
|
||||
return static_cast<char>(0x96);
|
||||
case 0x2014:
|
||||
return static_cast<char>(0x97);
|
||||
case 0x02dc:
|
||||
return static_cast<char>(0x98);
|
||||
case 0x2122:
|
||||
return static_cast<char>(0x99);
|
||||
case 0x0161:
|
||||
return static_cast<char>(0x9a);
|
||||
case 0x203a:
|
||||
return static_cast<char>(0x9b);
|
||||
case 0x0153:
|
||||
return static_cast<char>(0x9c);
|
||||
case 0x009d:
|
||||
return static_cast<char>(0x9d);
|
||||
case 0x017e:
|
||||
return static_cast<char>(0x9e);
|
||||
case 0x0178:
|
||||
return static_cast<char>(0x9f);
|
||||
case 0x00a0:
|
||||
return static_cast<char>(0xa0);
|
||||
case 0x00a1:
|
||||
return static_cast<char>(0xa1);
|
||||
case 0x00a2:
|
||||
return static_cast<char>(0xa2);
|
||||
case 0x00a3:
|
||||
return static_cast<char>(0xa3);
|
||||
case 0x00a4:
|
||||
return static_cast<char>(0xa4);
|
||||
case 0x00a5:
|
||||
return static_cast<char>(0xa5);
|
||||
case 0x00a6:
|
||||
return static_cast<char>(0xa6);
|
||||
case 0x00a7:
|
||||
return static_cast<char>(0xa7);
|
||||
case 0x00a8:
|
||||
return static_cast<char>(0xa8);
|
||||
case 0x00a9:
|
||||
return static_cast<char>(0xa9);
|
||||
case 0x00aa:
|
||||
return static_cast<char>(0xaa);
|
||||
case 0x00ab:
|
||||
return static_cast<char>(0xab);
|
||||
case 0x00ac:
|
||||
return static_cast<char>(0xac);
|
||||
case 0x00ad:
|
||||
return static_cast<char>(0xad);
|
||||
case 0x00ae:
|
||||
return static_cast<char>(0xae);
|
||||
case 0x00af:
|
||||
return static_cast<char>(0xaf);
|
||||
case 0x00b0:
|
||||
return static_cast<char>(0xb0);
|
||||
case 0x00b1:
|
||||
return static_cast<char>(0xb1);
|
||||
case 0x00b2:
|
||||
return static_cast<char>(0xb2);
|
||||
case 0x00b3:
|
||||
return static_cast<char>(0xb3);
|
||||
case 0x00b4:
|
||||
return static_cast<char>(0xb4);
|
||||
case 0x00b5:
|
||||
return static_cast<char>(0xb5);
|
||||
case 0x00b6:
|
||||
return static_cast<char>(0xb6);
|
||||
case 0x00b7:
|
||||
return static_cast<char>(0xb7);
|
||||
case 0x00b8:
|
||||
return static_cast<char>(0xb8);
|
||||
case 0x00b9:
|
||||
return static_cast<char>(0xb9);
|
||||
case 0x00ba:
|
||||
return static_cast<char>(0xba);
|
||||
case 0x00bb:
|
||||
return static_cast<char>(0xbb);
|
||||
case 0x00bc:
|
||||
return static_cast<char>(0xbc);
|
||||
case 0x00bd:
|
||||
return static_cast<char>(0xbd);
|
||||
case 0x00be:
|
||||
return static_cast<char>(0xbe);
|
||||
case 0x00bf:
|
||||
return static_cast<char>(0xbf);
|
||||
case 0x00c0:
|
||||
return static_cast<char>(0xc0);
|
||||
case 0x00c1:
|
||||
return static_cast<char>(0xc1);
|
||||
case 0x00c2:
|
||||
return static_cast<char>(0xc2);
|
||||
case 0x00c3:
|
||||
return static_cast<char>(0xc3);
|
||||
case 0x00c4:
|
||||
return static_cast<char>(0xc4);
|
||||
case 0x00c5:
|
||||
return static_cast<char>(0xc5);
|
||||
case 0x00c6:
|
||||
return static_cast<char>(0xc6);
|
||||
case 0x00c7:
|
||||
return static_cast<char>(0xc7);
|
||||
case 0x00c8:
|
||||
return static_cast<char>(0xc8);
|
||||
case 0x00c9:
|
||||
return static_cast<char>(0xc9);
|
||||
case 0x00ca:
|
||||
return static_cast<char>(0xca);
|
||||
case 0x00cb:
|
||||
return static_cast<char>(0xcb);
|
||||
case 0x00cc:
|
||||
return static_cast<char>(0xcc);
|
||||
case 0x00cd:
|
||||
return static_cast<char>(0xcd);
|
||||
case 0x00ce:
|
||||
return static_cast<char>(0xce);
|
||||
case 0x00cf:
|
||||
return static_cast<char>(0xcf);
|
||||
case 0x00d0:
|
||||
return static_cast<char>(0xd0);
|
||||
case 0x00d1:
|
||||
return static_cast<char>(0xd1);
|
||||
case 0x00d2:
|
||||
return static_cast<char>(0xd2);
|
||||
case 0x00d3:
|
||||
return static_cast<char>(0xd3);
|
||||
case 0x00d4:
|
||||
return static_cast<char>(0xd4);
|
||||
case 0x00d5:
|
||||
return static_cast<char>(0xd5);
|
||||
case 0x00d6:
|
||||
return static_cast<char>(0xd6);
|
||||
case 0x00d7:
|
||||
return static_cast<char>(0xd7);
|
||||
case 0x00d8:
|
||||
return static_cast<char>(0xd8);
|
||||
case 0x00d9:
|
||||
return static_cast<char>(0xd9);
|
||||
case 0x00da:
|
||||
return static_cast<char>(0xda);
|
||||
case 0x00db:
|
||||
return static_cast<char>(0xdb);
|
||||
case 0x00dc:
|
||||
return static_cast<char>(0xdc);
|
||||
case 0x00dd:
|
||||
return static_cast<char>(0xdd);
|
||||
case 0x00de:
|
||||
return static_cast<char>(0xde);
|
||||
case 0x00df:
|
||||
return static_cast<char>(0xdf);
|
||||
case 0x00e0:
|
||||
return static_cast<char>(0xe0);
|
||||
case 0x00e1:
|
||||
return static_cast<char>(0xe1);
|
||||
case 0x00e2:
|
||||
return static_cast<char>(0xe2);
|
||||
case 0x00e3:
|
||||
return static_cast<char>(0xe3);
|
||||
case 0x00e4:
|
||||
return static_cast<char>(0xe4);
|
||||
case 0x00e5:
|
||||
return static_cast<char>(0xe5);
|
||||
case 0x00e6:
|
||||
return static_cast<char>(0xe6);
|
||||
case 0x00e7:
|
||||
return static_cast<char>(0xe7);
|
||||
case 0x00e8:
|
||||
return static_cast<char>(0xe8);
|
||||
case 0x00e9:
|
||||
return static_cast<char>(0xe9);
|
||||
case 0x00ea:
|
||||
return static_cast<char>(0xea);
|
||||
case 0x00eb:
|
||||
return static_cast<char>(0xeb);
|
||||
case 0x00ec:
|
||||
return static_cast<char>(0xec);
|
||||
case 0x00ed:
|
||||
return static_cast<char>(0xed);
|
||||
case 0x00ee:
|
||||
return static_cast<char>(0xee);
|
||||
case 0x00ef:
|
||||
return static_cast<char>(0xef);
|
||||
case 0x00f0:
|
||||
return static_cast<char>(0xf0);
|
||||
case 0x00f1:
|
||||
return static_cast<char>(0xf1);
|
||||
case 0x00f2:
|
||||
return static_cast<char>(0xf2);
|
||||
case 0x00f3:
|
||||
return static_cast<char>(0xf3);
|
||||
case 0x00f4:
|
||||
return static_cast<char>(0xf4);
|
||||
case 0x00f5:
|
||||
return static_cast<char>(0xf5);
|
||||
case 0x00f6:
|
||||
return static_cast<char>(0xf6);
|
||||
case 0x00f7:
|
||||
return static_cast<char>(0xf7);
|
||||
case 0x00f8:
|
||||
return static_cast<char>(0xf8);
|
||||
case 0x00f9:
|
||||
return static_cast<char>(0xf9);
|
||||
case 0x00fa:
|
||||
return static_cast<char>(0xfa);
|
||||
case 0x00fb:
|
||||
return static_cast<char>(0xfb);
|
||||
case 0x00fc:
|
||||
return static_cast<char>(0xfc);
|
||||
case 0x00fd:
|
||||
return static_cast<char>(0xfd);
|
||||
case 0x00fe:
|
||||
return static_cast<char>(0xfe);
|
||||
case 0x00ff:
|
||||
return static_cast<char>(0xff);
|
||||
}
|
||||
|
||||
return static_cast<char>(0x3f);
|
||||
}
|
||||
|
||||
string
|
||||
from_utf8(string const& text)
|
||||
{
|
||||
return from_utf8(text.begin(), text.end());
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Internals::cp1252
|
||||
26
art/paperback/internals/cp1252.hxx
Normal file
26
art/paperback/internals/cp1252.hxx
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef art__paperback__internals__cp1252_hxx_
|
||||
#define art__paperback__internals__cp1252_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
|
||||
#include <art/unicode/reader.hxx>
|
||||
#include <art/unicode/utf8-decoder.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals::cp1252
|
||||
{
|
||||
|
||||
char
|
||||
from_unicode(uint32_t);
|
||||
|
||||
template<typename I>
|
||||
string
|
||||
from_utf8(I&&, I const&);
|
||||
|
||||
string
|
||||
from_utf8(string const&);
|
||||
|
||||
} // namespace Art::Paperback::Internals::cp1252
|
||||
|
||||
#include <art/paperback/internals/cp1252.txx>
|
||||
|
||||
#endif
|
||||
21
art/paperback/internals/cp1252.txx
Normal file
21
art/paperback/internals/cp1252.txx
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace Art::Paperback::Internals::cp1252
|
||||
{
|
||||
|
||||
template<typename I>
|
||||
string
|
||||
from_utf8(I&& first, I const& last)
|
||||
{
|
||||
stringstream str;
|
||||
str.imbue(std::locale::classic());
|
||||
|
||||
art::unicode::iterator_reader_t reader{first, last};
|
||||
art::unicode::utf8_decoder_t decoder{reader};
|
||||
|
||||
for (auto const& unicode : decoder) {
|
||||
str << from_unicode(unicode);
|
||||
}
|
||||
|
||||
return str.str();
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Internals::cp1252
|
||||
63
art/paperback/internals/document-catalog.cxx
Normal file
63
art/paperback/internals/document-catalog.cxx
Normal file
@@ -0,0 +1,63 @@
|
||||
#include <art/paperback/internals/document-catalog.hxx>
|
||||
|
||||
#include <art/paperback/document.hxx>
|
||||
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
#include <art/paperback/carousel/file.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals
|
||||
{
|
||||
|
||||
struct Document_catalog::Internal
|
||||
{
|
||||
Internal(Document& document)
|
||||
: document{document}
|
||||
{}
|
||||
|
||||
Document& document;
|
||||
|
||||
/// The page tree of the document. Allocated on first use.
|
||||
///
|
||||
optional<Page_tree> pages;
|
||||
|
||||
};
|
||||
|
||||
Document_catalog::
|
||||
Document_catalog(Create_new const&, Document& document)
|
||||
: internal{new Internal{document}}
|
||||
{
|
||||
document.file().catalog() = Carousel::Dictionary{};
|
||||
document.file().catalog().insert("Type", Carousel::Name{"Catalog"});
|
||||
}
|
||||
|
||||
Document_catalog::
|
||||
~Document_catalog() noexcept
|
||||
{}
|
||||
|
||||
Document&
|
||||
Document_catalog::
|
||||
document()
|
||||
{
|
||||
return internal->document;
|
||||
}
|
||||
|
||||
Document const&
|
||||
Document_catalog::
|
||||
document() const
|
||||
{
|
||||
return internal->document;
|
||||
}
|
||||
|
||||
Page_tree&
|
||||
Document_catalog::
|
||||
pages()
|
||||
{
|
||||
if (!internal->pages) {
|
||||
internal->pages.emplace(Page_tree::create_new, *this);
|
||||
document().file().catalog().insert("Pages", internal->pages->object());
|
||||
}
|
||||
|
||||
return *internal->pages;
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Internals
|
||||
54
art/paperback/internals/document-catalog.hxx
Normal file
54
art/paperback/internals/document-catalog.hxx
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef art__paperback__internals__document_catalog_hxx_
|
||||
#define art__paperback__internals__document_catalog_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/internals/page-tree.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals
|
||||
{
|
||||
|
||||
class Document_catalog
|
||||
{
|
||||
public:
|
||||
struct Create_new {};
|
||||
static constexpr Create_new const create_new{};
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Document_catalog(Create_new const&, Document&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Document_catalog() noexcept;
|
||||
|
||||
/// Access the parent document.
|
||||
///
|
||||
Document&
|
||||
document();
|
||||
|
||||
/// Access the parent document.
|
||||
///
|
||||
Document const&
|
||||
document() const;
|
||||
|
||||
/// Access the page tree.
|
||||
///
|
||||
Page_tree&
|
||||
pages();
|
||||
|
||||
private:
|
||||
Document_catalog(Document_catalog const&) = delete;
|
||||
Document_catalog(Document_catalog&&) = delete;
|
||||
Document_catalog& operator=(Document_catalog const&) = delete;
|
||||
Document_catalog& operator=(Document_catalog&&) = delete;
|
||||
|
||||
struct Internal;
|
||||
unique_ptr<Internal> internal;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Internals
|
||||
|
||||
#endif
|
||||
90
art/paperback/internals/font-collection.cxx
Normal file
90
art/paperback/internals/font-collection.cxx
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <art/paperback/internals/font-collection.hxx>
|
||||
#include <art/paperback/internals/resource-collection.hxx>
|
||||
|
||||
#include <art/paperback/except.hxx>
|
||||
#include <art/paperback/document.hxx>
|
||||
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
#include <art/paperback/carousel/file.hxx>
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
|
||||
#include <art/paperback/graphics/font.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals
|
||||
{
|
||||
|
||||
struct Font_collection::Internal
|
||||
{
|
||||
Internal(Resource_collection& parent)
|
||||
: parent{parent},
|
||||
object{Carousel::Dictionary{}},
|
||||
data{object_cast<Carousel::Dictionary>(object)}
|
||||
{}
|
||||
|
||||
Resource_collection& parent;
|
||||
Carousel::Object object;
|
||||
Carousel::Dictionary& data;
|
||||
|
||||
Carousel::Name
|
||||
get_next_local_name()
|
||||
{
|
||||
stringstream str;
|
||||
str << "F" << data.size();
|
||||
|
||||
return Carousel::Name{str.str()};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// This constructor creates a new font collection.
|
||||
///
|
||||
/// \param parent The parent resource collection.
|
||||
///
|
||||
Font_collection::
|
||||
Font_collection(Create_new const&,
|
||||
Resource_collection& parent)
|
||||
: internal{new Internal{parent}}
|
||||
{}
|
||||
|
||||
Font_collection::
|
||||
~Font_collection() noexcept
|
||||
{}
|
||||
|
||||
/// \return Returns a reference to the parent resource collection.
|
||||
///
|
||||
Resource_collection&
|
||||
Font_collection::
|
||||
resource_collection()
|
||||
{
|
||||
return internal->parent;
|
||||
}
|
||||
|
||||
/// \return Return the data object for this font collection.
|
||||
///
|
||||
Carousel::Object
|
||||
Font_collection::
|
||||
object()
|
||||
{
|
||||
return internal->object;
|
||||
}
|
||||
|
||||
/// \param font The font to embed.
|
||||
/// \return Returns the local name of the embedded font.
|
||||
///
|
||||
Carousel::Name
|
||||
Font_collection::
|
||||
embed(Graphics::Font& font)
|
||||
{
|
||||
auto local_name = internal->get_next_local_name();
|
||||
|
||||
if (internal->data.contains(local_name)) {
|
||||
raise<Internal_error>{} << "font local name already used";
|
||||
}
|
||||
|
||||
internal->data.insert(local_name, font.object());
|
||||
|
||||
return local_name;
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Internals
|
||||
54
art/paperback/internals/font-collection.hxx
Normal file
54
art/paperback/internals/font-collection.hxx
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef art__paperback__internals__font_collection_hxx_
|
||||
#define art__paperback__internals__font_collection_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals
|
||||
{
|
||||
|
||||
class Font_collection
|
||||
{
|
||||
public:
|
||||
struct Create_new {};
|
||||
static constexpr Create_new const create_new{};
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Font_collection(Create_new const&, Resource_collection&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Font_collection() noexcept;
|
||||
|
||||
/// Access the parent resource collection.
|
||||
///
|
||||
Resource_collection&
|
||||
resource_collection();
|
||||
|
||||
/// Get the object for this font collection.
|
||||
///
|
||||
Carousel::Object
|
||||
object();
|
||||
|
||||
/// Embed font in this font collection and return the local name of the
|
||||
/// embedded font.
|
||||
///
|
||||
Carousel::Name
|
||||
embed(Graphics::Font& f);
|
||||
|
||||
private:
|
||||
Font_collection(Font_collection const&) = delete;
|
||||
Font_collection(Font_collection&&) = delete;
|
||||
Font_collection& operator=(Font_collection const&) = delete;
|
||||
Font_collection& operator=(Font_collection&&) = delete;
|
||||
|
||||
private:
|
||||
struct Internal;
|
||||
unique_ptr<Internal> internal;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Internals
|
||||
|
||||
#endif
|
||||
19
art/paperback/internals/graphics-state.cxx
Normal file
19
art/paperback/internals/graphics-state.cxx
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <art/paperback/internals/graphics-state.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals
|
||||
{
|
||||
|
||||
Matrix
|
||||
apply(Matrix const& ctm, Matrix const& cm)
|
||||
{
|
||||
return Matrix{
|
||||
cm.a * ctm.a + cm.c * ctm.b,
|
||||
cm.b * ctm.a + cm.d * ctm.b,
|
||||
cm.a * ctm.c + cm.c * ctm.d,
|
||||
cm.b * ctm.c + cm.d * ctm.d,
|
||||
cm.a * ctm.e + cm.c * ctm.f + cm.e,
|
||||
cm.b * ctm.e + cm.d * ctm.f + cm.f
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Internals
|
||||
87
art/paperback/internals/graphics-state.hxx
Normal file
87
art/paperback/internals/graphics-state.hxx
Normal file
@@ -0,0 +1,87 @@
|
||||
#ifndef art__paperback__internals__graphics_state_hxx_
|
||||
#define art__paperback__internals__graphics_state_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
|
||||
#include <art/paperback/graphics/color.hxx>
|
||||
#include <art/paperback/graphics/font.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals
|
||||
{
|
||||
|
||||
struct Matrix
|
||||
{
|
||||
double a;
|
||||
double b;
|
||||
double c;
|
||||
double d;
|
||||
double e;
|
||||
double f;
|
||||
|
||||
};
|
||||
|
||||
Matrix
|
||||
apply(Matrix const&, Matrix const&);
|
||||
|
||||
enum class Text_rendering_mode
|
||||
{
|
||||
fill,
|
||||
stroke,
|
||||
fill_then_stroke,
|
||||
invisble,
|
||||
fill_clippping,
|
||||
stroke_clipping,
|
||||
fill_stroke_clipping,
|
||||
clipping
|
||||
|
||||
};
|
||||
|
||||
enum class Line_cap_style
|
||||
{
|
||||
butt_end,
|
||||
round_end,
|
||||
projecting_square_end
|
||||
|
||||
};
|
||||
|
||||
enum class Line_join_style
|
||||
{
|
||||
miter_join,
|
||||
round_join,
|
||||
bevel_join
|
||||
|
||||
};
|
||||
|
||||
struct Graphics_state
|
||||
{
|
||||
Matrix ctm;
|
||||
|
||||
Graphics::Color_space cs_stroke{Graphics::Color_space::device_grey};
|
||||
Graphics::Color_space cs_fill{Graphics::Color_space::device_grey};
|
||||
|
||||
Graphics::RGB rgb_stroke;
|
||||
Graphics::RGB rgb_fill;
|
||||
|
||||
double grey_stroke{};
|
||||
double grey_fill{};
|
||||
|
||||
double text_cspace{};
|
||||
double text_hspace{};
|
||||
double text_hscale{};
|
||||
double text_leading{};
|
||||
Graphics::Font* current_font{};
|
||||
double font_size{};
|
||||
Text_rendering_mode text_rendering_mode{};
|
||||
double ts_rise{};
|
||||
|
||||
double line_width{1.0};
|
||||
Line_cap_style line_cap{};
|
||||
Line_join_style line_join{};
|
||||
double miter_limit{10.0};
|
||||
double flatness{1.0};
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback::Internals
|
||||
|
||||
#endif
|
||||
147
art/paperback/internals/page-tree.cxx
Normal file
147
art/paperback/internals/page-tree.cxx
Normal file
@@ -0,0 +1,147 @@
|
||||
#include <art/paperback/internals/page-tree.hxx>
|
||||
|
||||
#include <art/paperback/internals/document-catalog.hxx>
|
||||
|
||||
#include <art/paperback/carousel/array.hxx>
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
#include <art/paperback/carousel/file.hxx>
|
||||
#include <art/paperback/carousel/integer.hxx>
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
|
||||
#include <art/paperback/document.hxx>
|
||||
#include <art/paperback/except.hxx>
|
||||
#include <art/paperback/page.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals
|
||||
{
|
||||
|
||||
struct Page_tree::Internal
|
||||
{
|
||||
Internal(Create_new const&,
|
||||
Document_catalog& document_catalog)
|
||||
: document_catalog{document_catalog},
|
||||
object{
|
||||
document_catalog.document().file().create_object<Carousel::Dictionary>(
|
||||
map<Carousel::Name, Carousel::Object>{
|
||||
{Carousel::Name{"Type"}, Carousel::Name{"Pages"}},
|
||||
{Carousel::Name{"Kids"}, Carousel::Array{}},
|
||||
{Carousel::Name{"Count"}, Carousel::Integer{0}}
|
||||
}
|
||||
)
|
||||
},
|
||||
data{object_cast<Carousel::Dictionary>(object)},
|
||||
kids{object_cast<Carousel::Array>(data.at("Kids"))},
|
||||
count{object_cast<Carousel::Integer>(data.at("Count"))}
|
||||
{}
|
||||
|
||||
Document_catalog& document_catalog;
|
||||
Carousel::Object object;
|
||||
Carousel::Dictionary& data;
|
||||
Carousel::Array& kids;
|
||||
Carousel::Integer& count;
|
||||
|
||||
// This is a map of all currently loaded pages, rather than all
|
||||
// pages in the document. It is populated on page creation or when
|
||||
// requesting a page not already loaded.
|
||||
//
|
||||
map<size_t, Page> pages;
|
||||
|
||||
};
|
||||
|
||||
/// \class Page_tree
|
||||
///
|
||||
/// \warning
|
||||
/// The current Page_tree implementation maintains a flat array of
|
||||
/// all pages in the tree. This is far from optimal. We should
|
||||
/// probably implement a self-balancing tree in the future.
|
||||
///
|
||||
|
||||
/// This constructor creates a new page tree.
|
||||
///
|
||||
/// \param document_catalog The parent document catalog.
|
||||
///
|
||||
Page_tree::
|
||||
Page_tree(Create_new const&, Document_catalog& document_catalog)
|
||||
: internal{new Internal{create_new, document_catalog}}
|
||||
{}
|
||||
|
||||
Page_tree::
|
||||
~Page_tree() noexcept
|
||||
{}
|
||||
|
||||
/// \return Returns a reference to the parent document catalog.
|
||||
///
|
||||
Document_catalog&
|
||||
Page_tree::
|
||||
document_catalog() const
|
||||
{
|
||||
return internal->document_catalog;
|
||||
}
|
||||
|
||||
Carousel::Object
|
||||
Page_tree::
|
||||
object() const
|
||||
{
|
||||
return internal->object;
|
||||
}
|
||||
|
||||
/// \param properties The properties of the new page.
|
||||
///
|
||||
Page&
|
||||
Page_tree::
|
||||
create_page(Page::Properties const& properties)
|
||||
{
|
||||
auto index = internal->kids.size();
|
||||
auto [it, unused] = internal->pages.try_emplace(
|
||||
index,
|
||||
Page::create_new,
|
||||
*this,
|
||||
properties
|
||||
);
|
||||
|
||||
internal->kids.push_back(it->second.object());
|
||||
internal->count = internal->kids.size();
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Page&
|
||||
Page_tree::
|
||||
get_page(size_t)
|
||||
{
|
||||
raise<Internal_error>{} << "function not implemented";
|
||||
}
|
||||
|
||||
Page const&
|
||||
Page_tree::
|
||||
get_page(size_t) const
|
||||
{
|
||||
raise<Internal_error>{} << "function not implemented";
|
||||
}
|
||||
|
||||
void
|
||||
Page_tree::
|
||||
erase_page(size_t)
|
||||
{
|
||||
raise<Internal_error>{} << "function not implemented";
|
||||
}
|
||||
|
||||
/// \param page_tree The page tree.
|
||||
/// \return Returns a reference to the parent document.
|
||||
///
|
||||
Document&
|
||||
document(Page_tree& page_tree)
|
||||
{
|
||||
return page_tree.document_catalog().document();
|
||||
}
|
||||
|
||||
/// \param page_tree The page tree.
|
||||
/// \return Returns a reference to the parent document.
|
||||
///
|
||||
Document const&
|
||||
document(Page_tree const& page_tree)
|
||||
{
|
||||
return page_tree.document_catalog().document();
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback::Internals
|
||||
83
art/paperback/internals/page-tree.hxx
Normal file
83
art/paperback/internals/page-tree.hxx
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef art__paperback__internals__page_tree_hxx_
|
||||
#define art__paperback__internals__page_tree_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
#include <art/paperback/page.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals
|
||||
{
|
||||
|
||||
/// Represents a PDF page tree.
|
||||
///
|
||||
class Page_tree
|
||||
{
|
||||
public:
|
||||
struct Create_new {};
|
||||
static constexpr Create_new const create_new{};
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Page_tree(Create_new const&, Document_catalog&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Page_tree() noexcept;
|
||||
|
||||
/// Access the parent document catalog.
|
||||
///
|
||||
Document_catalog&
|
||||
document_catalog() const;
|
||||
|
||||
/// Access the associated data object.
|
||||
///
|
||||
Carousel::Object
|
||||
object() const;
|
||||
|
||||
/// Create a new page and add it to the page tree.
|
||||
///
|
||||
Page&
|
||||
create_page(Page::Properties const&);
|
||||
|
||||
/// Get page.
|
||||
///
|
||||
Page&
|
||||
get_page(size_t);
|
||||
|
||||
/// Get page.
|
||||
///
|
||||
Page const&
|
||||
get_page(size_t) const;
|
||||
|
||||
/// Erase page.
|
||||
///
|
||||
void
|
||||
erase_page(size_t);
|
||||
|
||||
private:
|
||||
Page_tree(Page_tree const&) = delete;
|
||||
Page_tree(Page_tree&&) = delete;
|
||||
Page_tree& operator=(Page_tree const&) = delete;
|
||||
Page_tree& operator=(Page_tree&&) = delete;
|
||||
|
||||
private:
|
||||
struct Internal;
|
||||
unique_ptr<Internal> internal;
|
||||
|
||||
};
|
||||
|
||||
/// Get the document associated with a page tree.
|
||||
///
|
||||
Document&
|
||||
document(Page_tree&);
|
||||
|
||||
/// Get the document associated with a page tree.
|
||||
///
|
||||
Document const&
|
||||
document(Page_tree const&);
|
||||
|
||||
} // namespace Art::Paperback::Internals
|
||||
|
||||
#endif
|
||||
110
art/paperback/internals/resource-collection.cxx
Normal file
110
art/paperback/internals/resource-collection.cxx
Normal file
@@ -0,0 +1,110 @@
|
||||
#include <art/paperback/internals/resource-collection.hxx>
|
||||
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
#include <art/paperback/carousel/file.hxx>
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
|
||||
#include <art/paperback/document.hxx>
|
||||
#include <art/paperback/page.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals
|
||||
{
|
||||
|
||||
struct Resource_collection::Internal
|
||||
{
|
||||
Internal(Create_new const&, Page& page)
|
||||
: page{page},
|
||||
object{Carousel::Dictionary{}},
|
||||
data{object_cast<Carousel::Dictionary>(object)}
|
||||
{}
|
||||
|
||||
Page& page;
|
||||
|
||||
/// COS object for this resource collection.
|
||||
///
|
||||
Carousel::Object object;
|
||||
|
||||
/// Data dictionary for this resource collection.
|
||||
///
|
||||
Carousel::Dictionary& data;
|
||||
|
||||
/// Optional font collection, allocated on first use.
|
||||
///
|
||||
optional<Font_collection> fonts;
|
||||
|
||||
};
|
||||
|
||||
/// This contructor creates a new resource collection.
|
||||
///
|
||||
/// \param page The parent page of this resource collection.
|
||||
/// \param object The object for this resource collection.
|
||||
///
|
||||
Resource_collection::
|
||||
Resource_collection(Create_new, Page& page)
|
||||
: internal{new Internal{create_new, page}}
|
||||
{
|
||||
Carousel::Array procset;
|
||||
procset.push_back(Carousel::Name{"PDF"});
|
||||
procset.push_back(Carousel::Name{"Text"});
|
||||
procset.push_back(Carousel::Name{"ImageB"});
|
||||
procset.push_back(Carousel::Name{"ImageC"});
|
||||
procset.push_back(Carousel::Name{"ImageI"});
|
||||
|
||||
internal->data.insert("ProcSet", procset);
|
||||
}
|
||||
|
||||
Resource_collection::
|
||||
~Resource_collection() noexcept
|
||||
{}
|
||||
|
||||
Page&
|
||||
Resource_collection::
|
||||
page()
|
||||
{
|
||||
return internal->page;
|
||||
}
|
||||
|
||||
Page const&
|
||||
Resource_collection::
|
||||
page() const
|
||||
{
|
||||
return internal->page;
|
||||
}
|
||||
|
||||
Carousel::Object
|
||||
Resource_collection::
|
||||
object()
|
||||
{
|
||||
return internal->object;
|
||||
}
|
||||
|
||||
Font_collection&
|
||||
Resource_collection::
|
||||
fonts()
|
||||
{
|
||||
if (!internal->fonts) {
|
||||
internal->fonts.emplace(Font_collection::create_new, *this);
|
||||
internal->data.insert("Font", internal->fonts->object());
|
||||
}
|
||||
|
||||
return *internal->fonts;
|
||||
}
|
||||
|
||||
/// \return Returns a reference to the document.
|
||||
///
|
||||
Document&
|
||||
document(Resource_collection& resource_collection)
|
||||
{
|
||||
return document(resource_collection.page());
|
||||
}
|
||||
|
||||
/// \return Returns a reference to the document.
|
||||
///
|
||||
Document const&
|
||||
document(Resource_collection const& resource_collection)
|
||||
{
|
||||
return document(resource_collection.page());
|
||||
}
|
||||
|
||||
} // namespace cooper
|
||||
74
art/paperback/internals/resource-collection.hxx
Normal file
74
art/paperback/internals/resource-collection.hxx
Normal file
@@ -0,0 +1,74 @@
|
||||
#ifndef art__paperback__internals__resource_collection_hxx_
|
||||
#define art__paperback__internals__resource_collection_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
#include <art/paperback/internals/font-collection.hxx>
|
||||
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
|
||||
namespace Art::Paperback::Internals
|
||||
{
|
||||
|
||||
/// Represents a PDF resource collection.
|
||||
///
|
||||
class Resource_collection
|
||||
{
|
||||
public:
|
||||
struct Create_new {};
|
||||
static constexpr Create_new const create_new{};
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Resource_collection(Create_new, Page&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Resource_collection() noexcept;
|
||||
|
||||
/// Get the parent page.
|
||||
///
|
||||
Page&
|
||||
page();
|
||||
|
||||
/// Get the parent page.
|
||||
///
|
||||
Page const&
|
||||
page() const;
|
||||
|
||||
/// Get the object for this resource collection.
|
||||
///
|
||||
Carousel::Object
|
||||
object();
|
||||
|
||||
/// Get the font collection.
|
||||
///
|
||||
Font_collection&
|
||||
fonts();
|
||||
|
||||
private:
|
||||
Resource_collection(Resource_collection const&) = delete;
|
||||
Resource_collection(Resource_collection&&) = delete;
|
||||
Resource_collection& operator=(Resource_collection const&) = delete;
|
||||
Resource_collection& operator=(Resource_collection&&) = delete;
|
||||
|
||||
private:
|
||||
struct Internal;
|
||||
unique_ptr<Internal> internal;
|
||||
|
||||
};
|
||||
|
||||
/// Get the document associated with a resource collection.
|
||||
///
|
||||
Document&
|
||||
document(Resource_collection&);
|
||||
|
||||
/// Get the document associated with a resource collection.
|
||||
///
|
||||
Document const&
|
||||
document(Resource_collection const&);
|
||||
|
||||
} // namespace cooper
|
||||
|
||||
#endif
|
||||
131
art/paperback/page.cxx
Normal file
131
art/paperback/page.cxx
Normal file
@@ -0,0 +1,131 @@
|
||||
#include <art/paperback/page.hxx>
|
||||
#include <art/paperback/primitives.hxx>
|
||||
#include <art/paperback/document.hxx>
|
||||
|
||||
#include <art/paperback/carousel/array.hxx>
|
||||
#include <art/paperback/carousel/dictionary.hxx>
|
||||
#include <art/paperback/carousel/file.hxx>
|
||||
#include <art/paperback/carousel/name.hxx>
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
#include <art/paperback/carousel/stream.hxx>
|
||||
|
||||
#include <art/paperback/internals/document-catalog.hxx>
|
||||
#include <art/paperback/internals/page-tree.hxx>
|
||||
#include <art/paperback/internals/resource-collection.hxx>
|
||||
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
struct Page::Internal
|
||||
{
|
||||
Internal(Create_new const&,
|
||||
Internals::Page_tree& page_tree)
|
||||
: page_tree{page_tree},
|
||||
object{document(page_tree).file().create_object<Carousel::Dictionary>()},
|
||||
data{object_cast<Carousel::Dictionary>(object)},
|
||||
contents{document(page_tree).file().create_object<Carousel::Stream>()}
|
||||
{}
|
||||
|
||||
/// Parent page tree.
|
||||
///
|
||||
Internals::Page_tree& page_tree;
|
||||
|
||||
/// COS object for this page.
|
||||
///
|
||||
Carousel::Object object;
|
||||
|
||||
/// Data dictionary for this page.
|
||||
///
|
||||
Carousel::Dictionary& data;
|
||||
|
||||
/// The page's contents stream object.
|
||||
///
|
||||
Carousel::Object contents;
|
||||
|
||||
/// On-demand allocated resource collection.
|
||||
///
|
||||
optional<Internals::Resource_collection> resources;
|
||||
|
||||
};
|
||||
|
||||
/// This constructor creates a new page.
|
||||
///
|
||||
/// \param page_tree A reference to the parent page tree.
|
||||
/// \param properties The properties of the new page.
|
||||
///
|
||||
Page::
|
||||
Page(Create_new const&,
|
||||
Internals::Page_tree& page_tree,
|
||||
Properties const& properties)
|
||||
: internal{new Internal{create_new, page_tree}}
|
||||
{
|
||||
internal->data.insert("Type", Carousel::Name{"Page"});
|
||||
internal->data.insert("MediaBox", properties.media_box.to_array());
|
||||
internal->data.insert("Parent", page_tree.object());
|
||||
internal->data.insert("Contents", internal->contents);
|
||||
|
||||
internal->resources.emplace(
|
||||
Internals::Resource_collection::create_new, *this
|
||||
);
|
||||
|
||||
internal->data.insert("Resources", internal->resources->object());
|
||||
}
|
||||
|
||||
Page::
|
||||
~Page() noexcept
|
||||
{}
|
||||
|
||||
Internals::Page_tree&
|
||||
Page::
|
||||
page_tree() const
|
||||
{
|
||||
return internal->page_tree;
|
||||
}
|
||||
|
||||
Carousel::Object
|
||||
Page::
|
||||
object() const
|
||||
{
|
||||
return internal->object;
|
||||
}
|
||||
|
||||
Carousel::Stream&
|
||||
Page::
|
||||
contents()
|
||||
{
|
||||
return object_cast<Carousel::Stream>(internal->contents);
|
||||
|
||||
#if 0
|
||||
if (!internal->contents) {
|
||||
auto stream = document(*this).file().create_object<Carousel::Stream>();
|
||||
internal->data.insert("Contents", stream);
|
||||
internal->contents.emplace(stream);
|
||||
}
|
||||
|
||||
return *internal->contents;
|
||||
#endif
|
||||
}
|
||||
|
||||
Internals::Resource_collection&
|
||||
Page::
|
||||
resources()
|
||||
{
|
||||
if (!internal->resources) {
|
||||
}
|
||||
|
||||
return *internal->resources;
|
||||
}
|
||||
|
||||
Document&
|
||||
document(Page& page)
|
||||
{
|
||||
return document(page.page_tree());
|
||||
}
|
||||
|
||||
Document const&
|
||||
document(Page const& page)
|
||||
{
|
||||
return document(page.page_tree());
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback
|
||||
97
art/paperback/page.hxx
Normal file
97
art/paperback/page.hxx
Normal file
@@ -0,0 +1,97 @@
|
||||
#ifndef art__paperback__page_hxx_
|
||||
#define art__paperback__page_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
#include <art/paperback/primitives.hxx>
|
||||
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
class Page
|
||||
{
|
||||
public:
|
||||
struct Create_new {};
|
||||
static constexpr Create_new const create_new{};
|
||||
|
||||
/// Properties of a new page.
|
||||
///
|
||||
struct Properties
|
||||
{
|
||||
/// Specifies the page's media box (required).
|
||||
///
|
||||
Rectangle media_box;
|
||||
|
||||
/// Specifies the page's crop box.
|
||||
///
|
||||
optional<Rectangle> crop_box;
|
||||
|
||||
/// Specifies the page's bleed box.
|
||||
///
|
||||
optional<Rectangle> bleed_box;
|
||||
|
||||
/// Specifies the page's trim box.
|
||||
///
|
||||
optional<Rectangle> trim_box;
|
||||
|
||||
/// Specifies the page's art box.
|
||||
///
|
||||
optional<Rectangle> art_box;
|
||||
|
||||
};
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Page(Create_new const&,
|
||||
Internals::Page_tree&,
|
||||
Properties const&);
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
~Page() noexcept;
|
||||
|
||||
/// Access the parent page tree.
|
||||
///
|
||||
Internals::Page_tree&
|
||||
page_tree() const;
|
||||
|
||||
/// Access the page's object.
|
||||
///
|
||||
Carousel::Object
|
||||
object() const;
|
||||
|
||||
/// Access the page content stream.
|
||||
///
|
||||
Carousel::Stream&
|
||||
contents();
|
||||
|
||||
/// Access page resource collection.
|
||||
///
|
||||
Internals::Resource_collection&
|
||||
resources();
|
||||
|
||||
private:
|
||||
Page(Page const&) = delete;
|
||||
Page(Page&&) = delete;
|
||||
Page& operator=(Page const&) = delete;
|
||||
Page& operator=(Page&&) = delete;
|
||||
|
||||
private:
|
||||
struct Internal;
|
||||
unique_ptr<Internal> internal;
|
||||
|
||||
};
|
||||
|
||||
/// Get the document associated with a page.
|
||||
///
|
||||
Document&
|
||||
document(Page&);
|
||||
|
||||
/// Get the document associated with a page.
|
||||
///
|
||||
Document const&
|
||||
document(Page const&);
|
||||
|
||||
} // namespace Art::Paperback
|
||||
|
||||
#endif
|
||||
136
art/paperback/primitives.cxx
Normal file
136
art/paperback/primitives.cxx
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <art/paperback/primitives.hxx>
|
||||
|
||||
#include <art/paperback/carousel/array.hxx>
|
||||
#include <art/paperback/carousel/object.hxx>
|
||||
#include <art/paperback/carousel/real.hxx>
|
||||
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
Rectangle::
|
||||
Rectangle()
|
||||
{}
|
||||
|
||||
Rectangle::
|
||||
Rectangle(double left, double bottom, double right, double top)
|
||||
: _left{left},
|
||||
_bottom{bottom},
|
||||
_right{right},
|
||||
_top{top}
|
||||
{}
|
||||
|
||||
double
|
||||
Rectangle::
|
||||
left() const
|
||||
{
|
||||
return _left;
|
||||
}
|
||||
|
||||
double
|
||||
Rectangle::
|
||||
bottom() const
|
||||
{
|
||||
return _bottom;
|
||||
}
|
||||
|
||||
double
|
||||
Rectangle::
|
||||
right() const
|
||||
{
|
||||
return _right;
|
||||
}
|
||||
|
||||
double
|
||||
Rectangle::
|
||||
top() const
|
||||
{
|
||||
return _top;
|
||||
}
|
||||
|
||||
Carousel::Array
|
||||
Rectangle::
|
||||
to_array() const
|
||||
{
|
||||
Carousel::Array array{
|
||||
{
|
||||
Carousel::Real{left()},
|
||||
Carousel::Real{bottom()},
|
||||
Carousel::Real{right()},
|
||||
Carousel::Real{top()}
|
||||
}
|
||||
};
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
Rectangle
|
||||
Rectangle::
|
||||
letter()
|
||||
{
|
||||
return Rectangle{0, 0, 612, 792};
|
||||
}
|
||||
|
||||
Rectangle
|
||||
Rectangle::
|
||||
legal()
|
||||
{
|
||||
return Rectangle{0, 0, 612, 1008};
|
||||
}
|
||||
|
||||
Rectangle
|
||||
Rectangle::
|
||||
tabloid()
|
||||
{
|
||||
return Rectangle{0, 0, 792, 1224};
|
||||
}
|
||||
|
||||
Rectangle
|
||||
Rectangle::
|
||||
a3()
|
||||
{
|
||||
return Rectangle{0, 0, 842, 1191};
|
||||
}
|
||||
|
||||
Rectangle
|
||||
Rectangle::
|
||||
a4()
|
||||
{
|
||||
return Rectangle{0, 0, 595, 842};
|
||||
}
|
||||
|
||||
Rectangle
|
||||
Rectangle::
|
||||
a5()
|
||||
{
|
||||
return Rectangle{0, 0, 420, 595};
|
||||
}
|
||||
|
||||
Rectangle
|
||||
Rectangle::
|
||||
c3()
|
||||
{
|
||||
return Rectangle{0, 0, 918, 1296};
|
||||
}
|
||||
|
||||
Rectangle
|
||||
Rectangle::
|
||||
c4()
|
||||
{
|
||||
return Rectangle{0, 0, 649, 918};
|
||||
}
|
||||
|
||||
Rectangle
|
||||
Rectangle::
|
||||
c5()
|
||||
{
|
||||
return Rectangle{0, 0, 459, 649};
|
||||
}
|
||||
|
||||
Rectangle
|
||||
Rectangle::
|
||||
executive()
|
||||
{
|
||||
return Rectangle{0, 0, 522, 756};
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback
|
||||
104
art/paperback/primitives.hxx
Normal file
104
art/paperback/primitives.hxx
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef art__paperback__primitives_hxx_
|
||||
#define art__paperback__primitives_hxx_
|
||||
|
||||
#include <art/paperback/types.hxx>
|
||||
#include <art/paperback/forward.hxx>
|
||||
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
/// Represents a point in a two-dimensional space.
|
||||
///
|
||||
struct Point_2D
|
||||
{
|
||||
/// The X coordinate.
|
||||
///
|
||||
double x{};
|
||||
|
||||
/// The Y coordinate.
|
||||
///
|
||||
double y{};
|
||||
|
||||
};
|
||||
|
||||
/// Represents a PDF rectangle.
|
||||
///
|
||||
class Rectangle
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
Rectangle();
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
Rectangle(double, double, double, double);
|
||||
|
||||
double
|
||||
left() const;
|
||||
|
||||
double
|
||||
bottom() const;
|
||||
|
||||
double
|
||||
right() const;
|
||||
|
||||
double
|
||||
top() const;
|
||||
|
||||
/// Create COS array from rectangle.
|
||||
///
|
||||
Carousel::Array
|
||||
to_array() const;
|
||||
|
||||
static
|
||||
Rectangle
|
||||
letter();
|
||||
|
||||
static
|
||||
Rectangle
|
||||
legal();
|
||||
|
||||
static
|
||||
Rectangle
|
||||
tabloid();
|
||||
|
||||
static
|
||||
Rectangle
|
||||
a3();
|
||||
|
||||
static
|
||||
Rectangle
|
||||
a4();
|
||||
|
||||
static
|
||||
Rectangle
|
||||
a5();
|
||||
|
||||
static
|
||||
Rectangle
|
||||
c3();
|
||||
|
||||
static
|
||||
Rectangle
|
||||
c4();
|
||||
|
||||
static
|
||||
Rectangle
|
||||
c5();
|
||||
|
||||
static
|
||||
Rectangle
|
||||
executive();
|
||||
|
||||
private:
|
||||
double _left{};
|
||||
double _bottom{};
|
||||
double _right{};
|
||||
double _top{};
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback
|
||||
|
||||
#endif
|
||||
142
art/paperback/types.hxx
Normal file
142
art/paperback/types.hxx
Normal file
@@ -0,0 +1,142 @@
|
||||
#ifndef art__paperback__types_hxx_
|
||||
#define art__paperback__types_hxx_
|
||||
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
#ifndef GENERATING_DOCUMENTATION
|
||||
using std::initializer_list;
|
||||
|
||||
using std::invalid_argument;
|
||||
using std::logic_error;
|
||||
using std::runtime_error;
|
||||
|
||||
using std::int8_t;
|
||||
using std::int16_t;
|
||||
using std::int32_t;
|
||||
using std::int64_t;
|
||||
|
||||
using std::uint8_t;
|
||||
using std::uint16_t;
|
||||
using std::uint32_t;
|
||||
using std::uint64_t;
|
||||
|
||||
using std::unique_ptr;
|
||||
using std::shared_ptr;
|
||||
using std::make_shared;
|
||||
using std::make_unique;
|
||||
|
||||
using std::variant;
|
||||
using std::holds_alternative;
|
||||
|
||||
using std::deque;
|
||||
using std::list;
|
||||
using std::map;
|
||||
using std::stack;
|
||||
using std::vector;
|
||||
|
||||
using std::optional;
|
||||
using std::nullopt;
|
||||
|
||||
using std::string;
|
||||
|
||||
using std::istream;
|
||||
using std::ostream;
|
||||
using std::iostream;
|
||||
using std::streampos;
|
||||
using std::streamoff;
|
||||
using std::stringstream;
|
||||
|
||||
using std::cin;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
#endif
|
||||
|
||||
/// Index type.
|
||||
///
|
||||
using Index = uint32_t;
|
||||
|
||||
/// Generation type.
|
||||
///
|
||||
using Generation = uint16_t;
|
||||
|
||||
/// Represents the identity of an indirect object.
|
||||
///
|
||||
struct Identity
|
||||
{
|
||||
/// Constructor.
|
||||
///
|
||||
/// This constructor creates an identity with index 0 and
|
||||
/// generation 0.
|
||||
///
|
||||
Identity()
|
||||
{}
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param index The index of the identity.
|
||||
/// \param generation The generation of the identity.
|
||||
///
|
||||
Identity(Index index, Generation generation)
|
||||
: index{index}, generation{generation}
|
||||
{}
|
||||
|
||||
Index index{};
|
||||
Generation generation{};
|
||||
|
||||
/// Compare this identity with another.
|
||||
///
|
||||
/// \param other The identity to compare with.
|
||||
///
|
||||
bool
|
||||
operator==(Identity const& other) const
|
||||
{
|
||||
return index == other.index && generation == other.generation;
|
||||
}
|
||||
|
||||
/// Compare this identity with another.
|
||||
///
|
||||
/// \param other The identity to compare with.
|
||||
///
|
||||
bool
|
||||
operator!=(Identity const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
/// Compare this identity with another.
|
||||
///
|
||||
bool
|
||||
operator<(Identity const& other) const
|
||||
{
|
||||
if (index < other.index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (generation < other.generation) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace Art::Paperback
|
||||
|
||||
#endif
|
||||
37
art/paperback/version.hxx.in
Normal file
37
art/paperback/version.hxx.in
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef libart__paperback__version_hxx_
|
||||
#define libart__paperback__version_hxx_
|
||||
|
||||
// The numeric version format is AAAAABBBBBCCCCCDDDE where:
|
||||
//
|
||||
// AAAAA - major version number
|
||||
// BBBBB - minor version number
|
||||
// CCCCC - bugfix version number
|
||||
// DDD - alpha / beta (DDD + 500) version number
|
||||
// E - final (0) / snapshot (1)
|
||||
//
|
||||
// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example:
|
||||
//
|
||||
// Version AAAAABBBBBCCCCCDDDE
|
||||
//
|
||||
// 0.1.0 0000000001000000000
|
||||
// 0.1.2 0000000001000020000
|
||||
// 1.2.3 0000100002000030000
|
||||
// 2.2.0-a.1 0000200001999990010
|
||||
// 3.0.0-b.2 0000299999999995020
|
||||
// 2.2.0-a.1.z 0000200001999990011
|
||||
//
|
||||
#define LIBART_PAPERBACK_VERSION $libart_paperback.version.project_number$ULL
|
||||
#define LIBART_PAPERBACK_VERSION_STR "$libart_paperback.version.project$"
|
||||
#define LIBART_PAPERBACK_VERSION_ID "$libart_paperback.version.project_id$"
|
||||
#define LIBART_PAPERBACK_VERSION_FULL "$libart_paperback.version$"
|
||||
|
||||
#define LIBART_PAPERBACK_VERSION_MAJOR $libart_paperback.version.major$
|
||||
#define LIBART_PAPERBACK_VERSION_MINOR $libart_paperback.version.minor$
|
||||
#define LIBART_PAPERBACK_VERSION_PATCH $libart_paperback.version.patch$
|
||||
|
||||
#define LIBART_PAPERBACK_PRE_RELEASE $libart_paperback.version.pre_release$
|
||||
|
||||
#define LIBART_PAPERBACK_SNAPSHOT_SN $libart_paperback.version.snapshot_sn$ULL
|
||||
#define LIBART_PAPERBACK_SNAPSHOT_ID "$libart_paperback.version.snapshot_id$"
|
||||
|
||||
#endif
|
||||
71
art/paperback/visitor.hxx
Normal file
71
art/paperback/visitor.hxx
Normal file
@@ -0,0 +1,71 @@
|
||||
#ifndef libart_paperback__visitor_hxx_
|
||||
#define libart_paperback__visitor_hxx_
|
||||
|
||||
namespace Art::Paperback
|
||||
{
|
||||
|
||||
/// Virtual base class for visitors.
|
||||
///
|
||||
class Visitor
|
||||
{
|
||||
protected:
|
||||
Visitor() = default;
|
||||
|
||||
virtual
|
||||
~Visitor() noexcept = default;
|
||||
|
||||
};
|
||||
|
||||
/// Base class for visitors of type T.
|
||||
///
|
||||
template<typename T>
|
||||
class Basic_visitor
|
||||
{
|
||||
public:
|
||||
/// Visit.
|
||||
///
|
||||
/// \param visitee The visitee.
|
||||
///
|
||||
virtual
|
||||
void
|
||||
visit(T& visitee)
|
||||
{
|
||||
visit(const_cast<T const&>(visitee));
|
||||
}
|
||||
|
||||
/// Visit.
|
||||
///
|
||||
/// \param visitee The visitee.
|
||||
///
|
||||
virtual
|
||||
void
|
||||
visit(T const& visitee) = 0;
|
||||
|
||||
protected:
|
||||
/// Constructor.
|
||||
///
|
||||
Basic_visitor() = default;
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
virtual
|
||||
~Basic_visitor() noexcept = default;
|
||||
|
||||
};
|
||||
|
||||
/// Accept visitor on visitee.
|
||||
///
|
||||
/// \param visitee The visitee.
|
||||
/// \param v The visitor.
|
||||
///
|
||||
template<typename T>
|
||||
void
|
||||
accept(T const& visitee, Visitor& v)
|
||||
{
|
||||
auto& v_cast = dynamic_cast<Basic_visitor<T>&>(v);
|
||||
v_cast.visit(visitee);
|
||||
}
|
||||
|
||||
} // namespace Art::Paperback
|
||||
|
||||
#endif
|
||||
4
build/.gitignore
vendored
Normal file
4
build/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/config.build
|
||||
/root/
|
||||
/bootstrap/
|
||||
build/
|
||||
7
build/bootstrap.build
Normal file
7
build/bootstrap.build
Normal file
@@ -0,0 +1,7 @@
|
||||
project = libart-paperback
|
||||
|
||||
using version
|
||||
using config
|
||||
using test
|
||||
using install
|
||||
using dist
|
||||
6
build/export.build
Normal file
6
build/export.build
Normal file
@@ -0,0 +1,6 @@
|
||||
$out_root/
|
||||
{
|
||||
include art/paperback/
|
||||
}
|
||||
|
||||
export $out_root/art/paperback/$import.target
|
||||
16
build/root.build
Normal file
16
build/root.build
Normal file
@@ -0,0 +1,16 @@
|
||||
# Uncomment to suppress warnings coming from external libraries.
|
||||
#
|
||||
#cxx.internal.scope = current
|
||||
|
||||
cxx.std = latest
|
||||
|
||||
using cxx
|
||||
|
||||
hxx{*}: extension = hxx
|
||||
ixx{*}: extension = ixx
|
||||
txx{*}: extension = txx
|
||||
cxx{*}: extension = cxx
|
||||
|
||||
# The test target for cross-testing (running tests under Wine, etc).
|
||||
#
|
||||
test.target = $cxx.target
|
||||
31
buildfile
Normal file
31
buildfile
Normal file
@@ -0,0 +1,31 @@
|
||||
./: {art/ tests/ examples/} doc{README.md} legal{LICENSE} manifest
|
||||
|
||||
define doxyfile: file
|
||||
doxyfile{*}: extension = ""
|
||||
doxyfile{*}: in.substitution = lax
|
||||
|
||||
doxyfile{Doxyfile}: in{Doxyfile} $src_root/manifest
|
||||
{
|
||||
PRIVATE = "NO"
|
||||
}
|
||||
|
||||
doxyfile{Doxyfile-private}: in{Doxyfile} $src_root/manifest
|
||||
{
|
||||
PRIVATE = "YES"
|
||||
}
|
||||
|
||||
alias{docs}: doxyfile{Doxyfile} fsdir{$out_root/docs}
|
||||
{{
|
||||
diag doxygen $path($<[0])
|
||||
doxygen $path($<[0])
|
||||
}}
|
||||
|
||||
alias{docs-private}: doxyfile{Doxyfile-private} fsdir{$out_root/docs}
|
||||
{{
|
||||
diag doxygen $path($<[0])
|
||||
doxygen $path($<[0])
|
||||
}}
|
||||
|
||||
# Don't install tests.
|
||||
#
|
||||
tests/: install = false
|
||||
2683
docs/doxygen-awesome.css
Normal file
2683
docs/doxygen-awesome.css
Normal file
File diff suppressed because it is too large
Load Diff
116
docs/getting-started.md
Normal file
116
docs/getting-started.md
Normal file
@@ -0,0 +1,116 @@
|
||||
\page getting-started Getting Started
|
||||
|
||||
# Getting Started
|
||||
|
||||
Paperback uses the build2 build system, see https://build2.org/.
|
||||
|
||||
The first step is to include Paperback in your build2 project.
|
||||
|
||||
Add the following lines to your repositories.manifest file:
|
||||
|
||||
```
|
||||
:
|
||||
role: prerequisite
|
||||
location: https://code.helloryan.se/art/libart-paperback.git##HEAD
|
||||
```
|
||||
|
||||
And the following to your manifest file:
|
||||
|
||||
```
|
||||
depends: libart-paperback ^0.1.0-
|
||||
```
|
||||
|
||||
Finally, link your executable or library with Paperback:
|
||||
|
||||
```
|
||||
import libs =+ libart-paperback%lib{art-paperback}
|
||||
```
|
||||
|
||||
## Usage Example
|
||||
|
||||
The example below shows how to use Paperback to create a new PDF
|
||||
document and render some text on a page. More examples are available
|
||||
in `$src_root$/examples`.
|
||||
|
||||
```c++
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <art/paperback/document.hxx>
|
||||
#include <art/paperback/page.hxx>
|
||||
|
||||
#include <art/paperback/graphics/canvas.hxx>
|
||||
#include <art/paperback/graphics/standard-font.hxx>
|
||||
|
||||
using namespace std;
|
||||
using namespace Art::Paperback;
|
||||
using namespace Art::Paperback::Graphics;
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
if (argc != 2) {
|
||||
cerr << "usage: " << argv[0] << " <path>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
// Open the output file.
|
||||
//
|
||||
fstream fs{argv[1], std::ios_base::out | std::ios_base::binary};
|
||||
|
||||
// Create a new document.
|
||||
//
|
||||
Document document{Document::create_new, fs, 1, 4};
|
||||
Standard_font helvetica{document, "Helvetica"};
|
||||
|
||||
// Create a new A4 page.
|
||||
//
|
||||
double width = 595;
|
||||
double height = 842;
|
||||
|
||||
auto& page = document.create_page(Page::Properties{
|
||||
Rectangle{0, 0, width, height}
|
||||
});
|
||||
|
||||
// Create a page canvas and render some text.
|
||||
//
|
||||
{
|
||||
Canvas canvas{Canvas::clear, page};
|
||||
Canvas::Set_font use_font{canvas, helvetica, 14};
|
||||
|
||||
// Measure the width of the text, so we can center it.
|
||||
//
|
||||
auto tw = helvetica.get_text_width("Hello, world!");
|
||||
|
||||
auto text_width = (tw.width * 14) / 1000;
|
||||
auto capheight = (helvetica.get_capheight() * 14) / 1000;
|
||||
|
||||
double cx = (width / 2) - (text_width / 2);
|
||||
double cy = (height / 2) - (capheight / 2);
|
||||
|
||||
Canvas::Begin_text bt{canvas};
|
||||
|
||||
bt.move_text_pos(cx, cy);
|
||||
bt.show_text("Hello, world!");
|
||||
}
|
||||
|
||||
// Write the document explicitly to the underlying I/O stream.
|
||||
//
|
||||
// Otherwise, the Document destructor flushes it automatically,
|
||||
// ignoring any exceptions thrown.
|
||||
//
|
||||
document.flush();
|
||||
}
|
||||
catch (exception const& ex) {
|
||||
cerr << argv[0] << ": an error occurred: " << ex.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
catch (...) {
|
||||
cerr << argv[0] << ": an unknown error occurred\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
BIN
docs/icon.png
Normal file
BIN
docs/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
48
docs/logo.svg
Normal file
48
docs/logo.svg
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 400.32031 74.570312"
|
||||
width="400.32031"
|
||||
height="74.570312"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
sodipodi:docname="logo.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="namedview2"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="2.55625"
|
||||
inkscape:cx="200.0978"
|
||||
inkscape:cy="37.359413"
|
||||
inkscape:window-width="2192"
|
||||
inkscape:window-height="1164"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<!-- Main logo text -->
|
||||
<text
|
||||
x="197.44531"
|
||||
y="57.96875"
|
||||
font-family="Arial, sans-serif"
|
||||
font-size="80px"
|
||||
font-weight="700"
|
||||
text-anchor="middle"
|
||||
letter-spacing="-1px"
|
||||
id="text2"><tspan
|
||||
fill="#27ae60"
|
||||
id="tspan1">Paper</tspan><tspan
|
||||
fill="#2c3e50"
|
||||
id="tspan2">back</tspan></text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
10
docs/main.md
Normal file
10
docs/main.md
Normal file
@@ -0,0 +1,10 @@
|
||||
\mainpage Paperback
|
||||
|
||||
Paperback implements the PDF file format in C++, based on the PDF 1.4
|
||||
specification, available online at:
|
||||
https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.4.pdf.
|
||||
The ultimate goal is to provide a library capable of generating PDF
|
||||
files that conform to the PDF/A ISO 19005-1 standard.
|
||||
|
||||
See \ref getting-started for information on how to use Paperback in
|
||||
your application.
|
||||
55
docs/styles.css
Normal file
55
docs/styles.css
Normal file
@@ -0,0 +1,55 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Google+Sans+Code:ital,wght@0,300..800;1,300..800&family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap');
|
||||
|
||||
html
|
||||
{
|
||||
--font-family-normal: "Open Sans", sans-serif;
|
||||
--font-family-monospace: "Google Sans Code", monospace;
|
||||
--font-family-nav: "Open Sans", sans-serif;
|
||||
--font-family-title: "Open Sans", sans-serif;
|
||||
--font-family-toc: "Open Sans", sans-serif;
|
||||
--font-family-search: "Open Sans", sans-serif;
|
||||
--font-family-icon: "Open Sans", sans-serif;
|
||||
--font-family-tooltip: "Open Sans", sans-serif;
|
||||
|
||||
--page-link-color: #0057ae;
|
||||
--page-visited-link-color: #0057ae;
|
||||
}
|
||||
|
||||
#projectlogo img
|
||||
{
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
h1
|
||||
{
|
||||
margin-top: 2rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
h2
|
||||
{
|
||||
margin-top: 1.5rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h2:first-child
|
||||
{
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h3
|
||||
{
|
||||
margin-top: 1.5rem;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h3:first-child
|
||||
{
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
code
|
||||
{
|
||||
background: rgb(242, 242, 242);
|
||||
}
|
||||
8
examples/.gitignore
vendored
Normal file
8
examples/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Test executables.
|
||||
#
|
||||
driver
|
||||
|
||||
# Testscript output directories (can be symlinks).
|
||||
#
|
||||
test
|
||||
test-*
|
||||
2
examples/basics/.gitignore
vendored
Normal file
2
examples/basics/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
example
|
||||
*.pdf
|
||||
3
examples/basics/buildfile
Normal file
3
examples/basics/buildfile
Normal file
@@ -0,0 +1,3 @@
|
||||
import libs = libart-paperback%lib{art-paperback}
|
||||
|
||||
exe{example}: {hxx ixx txx cxx}{**} $libs
|
||||
102
examples/basics/example.cxx
Normal file
102
examples/basics/example.cxx
Normal file
@@ -0,0 +1,102 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <art/paperback/document.hxx>
|
||||
#include <art/paperback/document-information.hxx>
|
||||
#include <art/paperback/page.hxx>
|
||||
|
||||
#include <art/paperback/graphics/canvas.hxx>
|
||||
#include <art/paperback/graphics/standard-font.hxx>
|
||||
|
||||
using namespace std;
|
||||
using namespace Art::Paperback;
|
||||
using namespace Art::Paperback::Graphics;
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
if (argc != 2) {
|
||||
cerr << "usage: " << argv[0] << " <path>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
fstream fs{argv[1], ios_base::out | ios_base::binary};
|
||||
|
||||
// Create a new document.
|
||||
//
|
||||
Document document{Document::create_new, fs, 1, 4};
|
||||
document.information().set_title("Example Document");
|
||||
document.information().set_author("Example Author");
|
||||
document.information().set_subject("Example Subject");
|
||||
document.information().set_keywords("PDF for C++");
|
||||
document.information().set_creator("libart-paperback");
|
||||
document.information().set_producer("libart-paperback");
|
||||
|
||||
// Load one of the 14 standard fonts.
|
||||
//
|
||||
Standard_font font{document, "Helvetica-BoldOblique"};
|
||||
|
||||
double width = 595;
|
||||
double height = 842;
|
||||
|
||||
// Create a new page.
|
||||
//
|
||||
auto& page = document.create_page(Page::Properties{
|
||||
Rectangle{0, 0, width, height}
|
||||
});
|
||||
|
||||
// Create a new canvas and render some text on the page.
|
||||
//
|
||||
Canvas canvas{Canvas::clear, page};
|
||||
|
||||
// Set the font with a size of 14.
|
||||
//
|
||||
Canvas::Set_font use_font{canvas, font, 14};
|
||||
|
||||
// Render some text and graphics.
|
||||
//
|
||||
{
|
||||
// Measure the width of the text, so we can center it.
|
||||
//
|
||||
auto tw = font.get_text_width("Hello, world!");
|
||||
|
||||
auto text_width = (tw.width * 14) / 1000;
|
||||
auto capheight = (font.get_capheight() * 14) / 1000;
|
||||
|
||||
double cx = (width / 2) - (text_width / 2);
|
||||
double cy = (height / 2) - (capheight / 2);
|
||||
|
||||
{
|
||||
Canvas::Begin_text bt{canvas};
|
||||
|
||||
bt.move_text_pos(cx, cy);
|
||||
bt.show_text("Hello, world!");
|
||||
}
|
||||
|
||||
{
|
||||
Canvas::Path path{canvas, Canvas::Path::Paint_mode::stroke};
|
||||
|
||||
// Draw borders around the text.
|
||||
//
|
||||
path.move_to(cx - 10, cy - 10);
|
||||
path.line_to(cx - 10, cy + capheight + 10);
|
||||
path.line_to(cx + text_width + 10, cy + capheight + 10);
|
||||
path.line_to(cx + text_width + 10, cy - 10);
|
||||
}
|
||||
}
|
||||
|
||||
document.flush();
|
||||
}
|
||||
catch (exception const& ex) {
|
||||
cerr << argv[0] << ": error: " << ex.what() << '\n';
|
||||
}
|
||||
catch (int u) {
|
||||
cerr << argv[0] << ": " << u << '\n';
|
||||
}
|
||||
catch (...) {
|
||||
cerr << argv[0] << ": an unknown error occurred\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
4
examples/build/.gitignore
vendored
Normal file
4
examples/build/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/config.build
|
||||
/root/
|
||||
/bootstrap/
|
||||
build/
|
||||
5
examples/build/bootstrap.build
Normal file
5
examples/build/bootstrap.build
Normal file
@@ -0,0 +1,5 @@
|
||||
project = # Unnamed tests subproject.
|
||||
|
||||
using config
|
||||
using test
|
||||
using dist
|
||||
12
examples/build/root.build
Normal file
12
examples/build/root.build
Normal file
@@ -0,0 +1,12 @@
|
||||
cxx.std = latest
|
||||
|
||||
using cxx
|
||||
|
||||
hxx{*}: extension = hxx
|
||||
ixx{*}: extension = ixx
|
||||
txx{*}: extension = txx
|
||||
cxx{*}: extension = cxx
|
||||
|
||||
# No exe{} in this subproject is a test.
|
||||
#
|
||||
exe{*}: test = false
|
||||
1
examples/buildfile
Normal file
1
examples/buildfile
Normal file
@@ -0,0 +1 @@
|
||||
./: {*/ -build/}
|
||||
13
manifest
Normal file
13
manifest
Normal file
@@ -0,0 +1,13 @@
|
||||
: 1
|
||||
name: libart-paperback
|
||||
version: 0.1.0-a.0.z
|
||||
language: c++
|
||||
summary: libart-paperback PDF library for C++
|
||||
license: BSD-4-Clause
|
||||
description-file: README.md
|
||||
url: https://art.helloryan.se/
|
||||
email: art@helloryan.se
|
||||
depends: * build2 >= 0.17.0
|
||||
depends: * bpkg >= 0.17.0
|
||||
depends: libart-validation ^0.1.0-
|
||||
depends: libart-unicode ^0.1.0-
|
||||
10
repositories.manifest
Normal file
10
repositories.manifest
Normal file
@@ -0,0 +1,10 @@
|
||||
: 1
|
||||
summary: libart-paperback project repository
|
||||
|
||||
:
|
||||
role: prerequisite
|
||||
location: https://code.helloryan.se/art/libart-validation.git##HEAD
|
||||
|
||||
:
|
||||
role: prerequisite
|
||||
location: https://code.helloryan.se/art/libart-unicode.git##HEAD
|
||||
8
tests/.gitignore
vendored
Normal file
8
tests/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Test executables.
|
||||
#
|
||||
driver
|
||||
|
||||
# Testscript output directories (can be symlinks).
|
||||
#
|
||||
test
|
||||
test-*
|
||||
4
tests/build/.gitignore
vendored
Normal file
4
tests/build/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/config.build
|
||||
/root/
|
||||
/bootstrap/
|
||||
build/
|
||||
5
tests/build/bootstrap.build
Normal file
5
tests/build/bootstrap.build
Normal file
@@ -0,0 +1,5 @@
|
||||
project = # Unnamed tests subproject.
|
||||
|
||||
using config
|
||||
using test
|
||||
using dist
|
||||
16
tests/build/root.build
Normal file
16
tests/build/root.build
Normal file
@@ -0,0 +1,16 @@
|
||||
cxx.std = latest
|
||||
|
||||
using cxx
|
||||
|
||||
hxx{*}: extension = hxx
|
||||
ixx{*}: extension = ixx
|
||||
txx{*}: extension = txx
|
||||
cxx{*}: extension = cxx
|
||||
|
||||
# Every exe{} in this subproject is by default a test.
|
||||
#
|
||||
exe{*}: test = true
|
||||
|
||||
# The test target for cross-testing (running tests under Wine, etc).
|
||||
#
|
||||
test.target = $cxx.target
|
||||
1
tests/buildfile
Normal file
1
tests/buildfile
Normal file
@@ -0,0 +1 @@
|
||||
./: {*/ -build/}
|
||||
Reference in New Issue
Block a user