Skip to content

Commit

Permalink
release v2.0.17
Browse files Browse the repository at this point in the history
  • Loading branch information
jorisschellekens committed Jan 14, 2022
1 parent 3318fda commit c6ea9ec
Show file tree
Hide file tree
Showing 277 changed files with 1,237 additions and 158 deletions.
2 changes: 1 addition & 1 deletion borb/io/write/ascii_art/ascii_logo.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
borb version 2.0.16
borb version 2.0.17
Joris Schellekens
15 changes: 14 additions & 1 deletion borb/pdf/canvas/layout/hyphenation/hyphenation.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ class Hyphenation:
There are also a large number of exceptions, which further complicates matters.
"""

DO_NOT_HYPHENATE_BEFORE: int = 2
DO_NOT_HYPHENATE_AFTER: int = -2

def __init__(self, iso_language_code: str):
self._patterns: Trie = Trie()
self._min_prefix_length: int = 128
Expand Down Expand Up @@ -97,16 +100,26 @@ def hyphenate(self, s: str, hyphenation_character: str = chr(173)) -> str:
for k in range(self._min_suffix_length, self._max_suffix_length + 1):
if j == 0 and k == 0:
continue
if i + k > len(s):
if i + k >= len(s2):
continue
suffix: str = s2[i : (i + k)]
value: typing.Optional[int] = self._patterns[prefix + "0" + suffix]
if value:
hyphenation_info[i] = max(hyphenation_info[i], value)
s3: str = ""
for i in range(1, len(hyphenation_info) - 1):
# obey DO_NOT_HYPHENATE_BEFORE
if (i - 1) <= Hyphenation.DO_NOT_HYPHENATE_BEFORE:
s3 += s2[i]
continue
# obey DO_NOT_HYPHENATE_AFTER
if (i - 1) >= len(s) + Hyphenation.DO_NOT_HYPHENATE_AFTER:
s3 += s2[i]
continue
# do not allow split on last 2, or first 2 characters
if hyphenation_info[i] % 2 == 1:
s3 += hyphenation_character + s2[i]
else:
s3 += s2[i]
# return
return s3
17 changes: 17 additions & 0 deletions borb/pdf/canvas/layout/page_layout/box_layout.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
This implementation of PageLayout adds left/right/top/bottom margins to a Page
and lays out the content on the Page as if there were multiple boxes to flow text, images, etc into.
Once a box is full, the next box is automatically selected, although the next column can be manually selected.
"""
import typing

from borb.pdf.canvas.geometry.rectangle import Rectangle
Expand All @@ -7,10 +15,19 @@


class BoxLayout(PageLayout):
"""
This implementation of PageLayout adds left/right/top/bottom margins to a Page
and lays out the content on the Page as if there were multiple boxes to flow text, images, etc into.
Once a box is full, the next box is automatically selected, although the next column can be manually selected.
"""

def __init__(self, page: Page, boxes: typing.List[Rectangle]):
super(BoxLayout, self).__init__(page)
assert len(boxes) > 0
self._boxes: typing.List[Rectangle] = boxes

def add(self, layout_element: LayoutElement) -> "PageLayout":
"""
This method adds a `LayoutElement` to the current `Page`.
"""
return self
43 changes: 43 additions & 0 deletions borb/pdf/canvas/layout/shape/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""
This file is part of the borb (R) project.
Copyright (c) 2020-2040 borb Group NV
Authors: Joris Schellekens, et al.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License version 3
as published by the Free Software Foundation with the addition of the
following permission added to Section 15 as permitted in Section 7(a):
FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
BORB GROUP. BORB GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
OF THIRD PARTY RIGHTS
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program; if not, see http://www.gnu.org/licenses or write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA, 02110-1301 USA.
The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License.
In accordance with Section 7(b) of the GNU Affero General Public License,
a covered work must retain the producer line in every PDF that is created
or manipulated using borb.
You can be released from the requirements of the license by purchasing
a commercial license. Buying such a license is mandatory as soon as you
develop commercial activities involving the borb software without
disclosing the source code of your own applications.
These activities include: offering paid services to customers as an ASP,
serving PDFs on the fly in a web application, shipping borb with a closed
source product.
For more information, please contact borb Software Corp. at this
address: [email protected]
"""
143 changes: 143 additions & 0 deletions borb/pdf/canvas/layout/shape/disjoint_shape.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
This class represents a generic disjoint shape (specified by a List of lines).
It has convenience methods to calculate width and height, perform scaling, etc
"""

from decimal import Decimal

import typing

from borb.pdf.canvas.color.color import HexColor, Color, X11Color
from borb.pdf.canvas.geometry.rectangle import Rectangle
from borb.pdf.canvas.layout.layout_element import Alignment, LayoutElement
from borb.pdf.page.page import Page


class DisjointShape(LayoutElement):
"""
This class represents a generic disjoint shape (specified by a List of lines).
It has convenience methods to calculate width and height, perform scaling, etc
"""

def __init__(
self,
lines: typing.List[
typing.Tuple[typing.Tuple[Decimal, Decimal], typing.Tuple[Decimal, Decimal]]
],
stroke_color: Color = HexColor("000000"),
line_width: Decimal = Decimal(0),
horizontal_alignment: Alignment = Alignment.LEFT,
vertical_alignment: Alignment = Alignment.TOP,
preserve_aspect_ratio: bool = True,
):
super(DisjointShape, self).__init__(
horizontal_alignment=horizontal_alignment,
vertical_alignment=vertical_alignment,
)
assert len(lines) > 0
self._lines = lines
self._stroke_color = stroke_color
self._line_width = line_width
self._preserve_aspect_ratio = preserve_aspect_ratio

def get_width(self) -> Decimal:
"""
This function returns the width of this DisjointShape
"""
min_x = min([min(x[0][0], x[1][0]) for x in self._lines])
max_x = max([max(x[0][0], x[1][0]) for x in self._lines])
return max_x - min_x

def get_height(self) -> Decimal:
"""
This function returns the height of this DisjointShape
"""
min_y = min([min(x[0][1], x[1][1]) for x in self._lines])
max_y = max([max(x[0][1], x[1][1]) for x in self._lines])
return max_y - min_y

def scale_to_fit(self, max_width: Decimal, max_height: Decimal) -> "DisjointShape":
"""
This method scales this DisjointShape to fit a given max. width / height
"""
w_scale = max_width / self.get_width()
h_scale = max_height / self.get_height()
if self._preserve_aspect_ratio:
w_scale = min(w_scale, h_scale)
h_scale = w_scale
if w_scale < 1:
self._lines = [
((x[0][0] * w_scale, x[0][1]), (x[1][0] * w_scale, x[1][1]))
for x in self._lines
]
if h_scale < 1:
self._lines = [
((x[0][0], x[0][1] * h_scale), (x[1][0], x[1][1] * h_scale))
for x in self._lines
]
return self

def translate_to_align(
self, lower_left_x: Decimal, lower_left_y: Decimal
) -> "DisjointShape":
"""
This method translates this DisjointShape so its lower left corner aligns with the given coordinates
"""
min_x = min([min(x[0][0], x[1][0]) for x in self._lines])
min_y = min([min(x[0][1], x[1][1]) for x in self._lines])
delta_x = lower_left_x - min_x
delta_y = lower_left_y - min_y
self._lines = [
(
(x[0][0] + delta_x, x[0][1] + delta_y),
(x[1][0] + delta_x, x[1][1] + delta_y),
)
for x in self._lines
]
return self

def _do_layout_without_padding(
self, page: Page, bounding_box: Rectangle
) -> Rectangle:

# scale to fit
self.scale_to_fit(bounding_box.width, bounding_box.height)

# translate points to fit in box
self.translate_to_align(
bounding_box.x, bounding_box.y + bounding_box.height - self.get_height()
)

# write content
stroke_rgb = (self._stroke_color or X11Color("Black")).to_rgb()
content = "q %f %f %f RG %d w " % (
Decimal(stroke_rgb.red),
Decimal(stroke_rgb.green),
Decimal(stroke_rgb.blue),
self._line_width,
)
for l in self._lines:
content += " %f %f m %f %f l " % (l[0][0], l[0][1], l[1][0], l[1][1])

# stroke
content += " S Q"

# append to page
self._append_to_content_stream(page, content)

# calculate bounding box
layout_rect = Rectangle(
bounding_box.x,
bounding_box.y + bounding_box.height - self.get_height(),
self.get_width(),
self.get_height(),
)

# set bounding box
self.set_bounding_box(layout_rect)

# return
return layout_rect
Loading

0 comments on commit c6ea9ec

Please sign in to comment.