Changeset - 391fde5447de
[Not reviewed]
0 2 0
Brett Smith - 4 years ago 2021-01-09 15:09:08
brettcsmith@brettcsmith.org
fields: TextField.set_value() accepts numeric types.
2 files changed with 28 insertions and 1 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/pdfforms/fields.py
Show inline comments
 
"""fields.py - Python classes to read and write PDF form data"""
 
# Copyright © 2020  Brett Smith
 
# License: AGPLv3-or-later WITH Beancount-Plugin-Additional-Permission-1.0
 
#
 
# Full copyright and licensing details can be found at toplevel file
 
# LICENSE.txt in the repository.
 

	
 
import enum
 
import functools
 

	
 
from decimal import Decimal
 

	
 
import babel.numbers  # type:ignore[import]
 

	
 
from pdfminer.pdftypes import resolve1  # type:ignore[import]
 
from pdfminer import psparser  # type:ignore[import]
 
from . import utils as pdfutils
 
from .errors import PDFKeyError, PDFSpecError
 

	
 
from typing import (
 
    Any,
 
    Iterator,
 
    Optional,
 
    Mapping,
 
    MutableMapping,
 
    Sequence,
 
    Tuple,
 
    Union,
 
    cast,
 
)
 

	
 
FieldSource = MutableMapping[str, Any]
 

	
 
class FieldFlags(enum.IntFlag):
 
    # Flags for all fields
 
    ReadOnly = 2 ** 0
 
    Required = 2 ** 1
 
    NoExport = 2 ** 2
 
    # Flags for buttons
 
    NoToggleToOff = 2 ** 14
 
    Radio = 2 ** 15
...
 
@@ -229,17 +234,19 @@ class TextField(FormField):
 

	
 
    def _decode(self, value: Any) -> Optional[str]:
 
        if value is None:
 
            return value
 
        elif isinstance(value, bytes):
 
            return pdfutils.decode_text(value)
 
        else:
 
            raise PDFSpecError("text field value is not bytes")
 

	
 
    def value(self) -> Optional[str]:
 
        return self._decode(super().value())
 

	
 
    def set_value(self, value: Optional[str]) -> None:
 
    def set_value(self, value: Union[str, Decimal, float, int, None]) -> None:
 
        if isinstance(value, (Decimal, float, int)):
 
            value = cast(str, babel.numbers.format_decimal(value))
 
        super().set_value(None if value is None else pdfutils.encode_text(value))
 

	
 
    def fill_value(self) -> Optional[str]:
 
        return self._decode(super().fill_value())
tests/test_pdfforms_fields.py
Show inline comments
 
"""test_pdfforms_fields.py - Unit tests for PDF forms manipulation"""
 
# Copyright © 2020  Brett Smith
 
# License: AGPLv3-or-later WITH Beancount-Plugin-Additional-Permission-1.0
 
#
 
# Full copyright and licensing details can be found at toplevel file
 
# LICENSE.txt in the repository.
 

	
 
import codecs
 
import itertools
 

	
 
import pytest
 

	
 
from decimal import Decimal
 

	
 
from pdfminer.psparser import PSLiteral
 

	
 
from conservancy_beancount.pdfforms import fields as fieldsmod
 

	
 
def field_source(
 
        name=None,
 
        value=None,
 
        field_type=None,
 
        flags=None,
 
        parent=None,
 
        kids=None,
 
        *,
...
 
@@ -240,24 +242,42 @@ def test_text_value_none():
 
@pytest.mark.parametrize('text,bprefix', [
 
    ('ASCII test', b''),
 
    ('UTF—16 test', codecs.BOM_UTF16_BE),
 
])
 
def test_text_set_value(text, bprefix):
 
    source = field_source(field_type='Tx')
 
    field = fieldsmod.TextField(source)
 
    field.set_value(text)
 
    assert field.value() == text
 
    actual = fieldsmod.FormField.value(field)
 
    assert actual == bprefix + text.encode('utf-16be' if bprefix else 'ascii')
 

	
 
@pytest.mark.parametrize('expected', [
 
    '0',
 
    '32',
 
    '32.45',
 
    '32,768',
 
    '32,768.95',
 
])
 
def test_text_set_value_numeric(expected):
 
    num_s = expected.replace(',', '')
 
    field = fieldsmod.TextField({})
 
    num_types = [Decimal, float]
 
    if '.' not in expected:
 
        num_types.append(int)
 
    for num_type in num_types:
 
        field.set_value(num_type(num_s))
 
        assert field.value() == expected
 
        field.set_value(None)
 

	
 
def test_text_set_value_none():
 
    source = field_source('t', b'set None test', 'Tx')
 
    field = fieldsmod.TextField(source)
 
    field.set_value(None)
 
    assert fieldsmod.FormField.value(field) is None
 

	
 
def test_empty_as_filled_fdf():
 
    source = field_source()
 
    field = fieldsmod.FormField(source)
 
    assert field.as_filled_fdf() == {}
 

	
 
@pytest.mark.parametrize('field_type,field_class,set_value', [
0 comments (0 inline, 0 general)