diff --git a/tools/kothic/drules_struct_pb2.py b/tools/kothic/drules_struct_pb2.py new file mode 100644 index 0000000000..47d1db5510 --- /dev/null +++ b/tools/kothic/drules_struct_pb2.py @@ -0,0 +1,817 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! + +from google.protobuf import descriptor +from google.protobuf import message +from google.protobuf import reflection +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + + +DESCRIPTOR = descriptor.FileDescriptor( + name='drules_struct.proto', + package='', + serialized_pb='\n\x13\x64rules_struct.proto\"*\n\x0c\x44\x61shDotProto\x12\n\n\x02\x64\x64\x18\x01 \x03(\x01\x12\x0e\n\x06offset\x18\x02 \x01(\x01\":\n\x0cPathSymProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04step\x18\x02 \x02(\x01\x12\x0e\n\x06offset\x18\x03 \x01(\x01\"\xaf\x01\n\rLineRuleProto\x12\r\n\x05width\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1e\n\x07\x64\x61shdot\x18\x03 \x01(\x0b\x32\r.DashDotProto\x12\x10\n\x08priority\x18\x04 \x02(\x05\x12\x1e\n\x07pathsym\x18\x05 \x01(\x0b\x32\r.PathSymProto\x12\x17\n\x04join\x18\x06 \x01(\x0e\x32\t.LineJoin\x12\x15\n\x03\x63\x61p\x18\x07 \x01(\x0e\x32\x08.LineCap\"\x9c\x01\n\x0cLineDefProto\x12\r\n\x05width\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1e\n\x07\x64\x61shdot\x18\x03 \x01(\x0b\x32\r.DashDotProto\x12\x1e\n\x07pathsym\x18\x04 \x01(\x0b\x32\r.PathSymProto\x12\x17\n\x04join\x18\x06 \x01(\x0e\x32\t.LineJoin\x12\x15\n\x03\x63\x61p\x18\x07 \x01(\x0e\x32\x08.LineCap\"O\n\rAreaRuleProto\x12\r\n\x05\x63olor\x18\x01 \x02(\r\x12\x1d\n\x06\x62order\x18\x02 \x01(\x0b\x32\r.LineDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"I\n\x0fSymbolRuleProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x16\n\x0e\x61pply_for_type\x18\x02 \x01(\x05\x12\x10\n\x08priority\x18\x03 \x02(\x05\"j\n\x0f\x43\x61ptionDefProto\x12\x0e\n\x06height\x18\x01 \x02(\x05\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x14\n\x0cstroke_color\x18\x03 \x01(\r\x12\x10\n\x08offset_x\x18\x04 \x01(\x05\x12\x10\n\x08offset_y\x18\x05 \x01(\x05\"l\n\x10\x43\x61ptionRuleProto\x12!\n\x07primary\x18\x01 \x02(\x0b\x32\x10.CaptionDefProto\x12#\n\tsecondary\x18\x02 \x01(\x0b\x32\x10.CaptionDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"a\n\x0f\x43ircleRuleProto\x12\x0e\n\x06radius\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1d\n\x06\x62order\x18\x03 \x01(\x0b\x32\r.LineDefProto\x12\x10\n\x08priority\x18\x04 \x02(\x05\"m\n\x11PathTextRuleProto\x12!\n\x07primary\x18\x01 \x02(\x0b\x32\x10.CaptionDefProto\x12#\n\tsecondary\x18\x02 \x01(\x0b\x32\x10.CaptionDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"\xed\x01\n\x10\x44rawElementProto\x12\r\n\x05scale\x18\x01 \x02(\x05\x12\x1d\n\x05lines\x18\x02 \x03(\x0b\x32\x0e.LineRuleProto\x12\x1c\n\x04\x61rea\x18\x03 \x01(\x0b\x32\x0e.AreaRuleProto\x12 \n\x06symbol\x18\x04 \x01(\x0b\x32\x10.SymbolRuleProto\x12\"\n\x07\x63\x61ption\x18\x05 \x01(\x0b\x32\x11.CaptionRuleProto\x12 \n\x06\x63ircle\x18\x06 \x01(\x0b\x32\x10.CircleRuleProto\x12%\n\tpath_text\x18\x07 \x01(\x0b\x32\x12.PathTextRuleProto\"G\n\x13\x43lassifElementProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\"\n\x07\x65lement\x18\x02 \x03(\x0b\x32\x11.DrawElementProto\"4\n\x0e\x43ontainerProto\x12\"\n\x04\x63ont\x18\x01 \x03(\x0b\x32\x14.ClassifElementProto*4\n\x08LineJoin\x12\r\n\tROUNDJOIN\x10\x00\x12\r\n\tBEVELJOIN\x10\x01\x12\n\n\x06NOJOIN\x10\x02*3\n\x07LineCap\x12\x0c\n\x08ROUNDCAP\x10\x00\x12\x0b\n\x07\x42UTTCAP\x10\x01\x12\r\n\tSQUARECAP\x10\x02\x42\x02H\x03') + +_LINEJOIN = descriptor.EnumDescriptor( + name='LineJoin', + full_name='LineJoin', + filename=None, + file=DESCRIPTOR, + values=[ + descriptor.EnumValueDescriptor( + name='ROUNDJOIN', index=0, number=0, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='BEVELJOIN', index=1, number=1, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='NOJOIN', index=2, number=2, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=1415, + serialized_end=1467, +) + + +_LINECAP = descriptor.EnumDescriptor( + name='LineCap', + full_name='LineCap', + filename=None, + file=DESCRIPTOR, + values=[ + descriptor.EnumValueDescriptor( + name='ROUNDCAP', index=0, number=0, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='BUTTCAP', index=1, number=1, + options=None, + type=None), + descriptor.EnumValueDescriptor( + name='SQUARECAP', index=2, number=2, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=1469, + serialized_end=1520, +) + + +ROUNDJOIN = 0 +BEVELJOIN = 1 +NOJOIN = 2 +ROUNDCAP = 0 +BUTTCAP = 1 +SQUARECAP = 2 + + +_DASHDOTPROTO = descriptor.Descriptor( + name='DashDotProto', + full_name='DashDotProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='dd', full_name='DashDotProto.dd', index=0, + number=1, type=1, cpp_type=5, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='offset', full_name='DashDotProto.offset', index=1, + number=2, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=23, + serialized_end=65, +) + + +_PATHSYMPROTO = descriptor.Descriptor( + name='PathSymProto', + full_name='PathSymProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='name', full_name='PathSymProto.name', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=unicode("", "utf-8"), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='step', full_name='PathSymProto.step', index=1, + number=2, type=1, cpp_type=5, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='offset', full_name='PathSymProto.offset', index=2, + number=3, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=67, + serialized_end=125, +) + + +_LINERULEPROTO = descriptor.Descriptor( + name='LineRuleProto', + full_name='LineRuleProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='width', full_name='LineRuleProto.width', index=0, + number=1, type=1, cpp_type=5, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='color', full_name='LineRuleProto.color', index=1, + number=2, type=13, cpp_type=3, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='dashdot', full_name='LineRuleProto.dashdot', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='priority', full_name='LineRuleProto.priority', index=3, + number=4, type=5, cpp_type=1, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='pathsym', full_name='LineRuleProto.pathsym', index=4, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='join', full_name='LineRuleProto.join', index=5, + number=6, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='cap', full_name='LineRuleProto.cap', index=6, + number=7, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=128, + serialized_end=303, +) + + +_LINEDEFPROTO = descriptor.Descriptor( + name='LineDefProto', + full_name='LineDefProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='width', full_name='LineDefProto.width', index=0, + number=1, type=1, cpp_type=5, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='color', full_name='LineDefProto.color', index=1, + number=2, type=13, cpp_type=3, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='dashdot', full_name='LineDefProto.dashdot', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='pathsym', full_name='LineDefProto.pathsym', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='join', full_name='LineDefProto.join', index=4, + number=6, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='cap', full_name='LineDefProto.cap', index=5, + number=7, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=306, + serialized_end=462, +) + + +_AREARULEPROTO = descriptor.Descriptor( + name='AreaRuleProto', + full_name='AreaRuleProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='color', full_name='AreaRuleProto.color', index=0, + number=1, type=13, cpp_type=3, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='border', full_name='AreaRuleProto.border', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='priority', full_name='AreaRuleProto.priority', index=2, + number=3, type=5, cpp_type=1, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=464, + serialized_end=543, +) + + +_SYMBOLRULEPROTO = descriptor.Descriptor( + name='SymbolRuleProto', + full_name='SymbolRuleProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='name', full_name='SymbolRuleProto.name', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=unicode("", "utf-8"), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='apply_for_type', full_name='SymbolRuleProto.apply_for_type', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='priority', full_name='SymbolRuleProto.priority', index=2, + number=3, type=5, cpp_type=1, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=545, + serialized_end=618, +) + + +_CAPTIONDEFPROTO = descriptor.Descriptor( + name='CaptionDefProto', + full_name='CaptionDefProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='height', full_name='CaptionDefProto.height', index=0, + number=1, type=5, cpp_type=1, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='color', full_name='CaptionDefProto.color', index=1, + number=2, type=13, cpp_type=3, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='stroke_color', full_name='CaptionDefProto.stroke_color', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='offset_x', full_name='CaptionDefProto.offset_x', index=3, + number=4, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='offset_y', full_name='CaptionDefProto.offset_y', index=4, + number=5, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=620, + serialized_end=726, +) + + +_CAPTIONRULEPROTO = descriptor.Descriptor( + name='CaptionRuleProto', + full_name='CaptionRuleProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='primary', full_name='CaptionRuleProto.primary', index=0, + number=1, type=11, cpp_type=10, label=2, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='secondary', full_name='CaptionRuleProto.secondary', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='priority', full_name='CaptionRuleProto.priority', index=2, + number=3, type=5, cpp_type=1, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=728, + serialized_end=836, +) + + +_CIRCLERULEPROTO = descriptor.Descriptor( + name='CircleRuleProto', + full_name='CircleRuleProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='radius', full_name='CircleRuleProto.radius', index=0, + number=1, type=1, cpp_type=5, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='color', full_name='CircleRuleProto.color', index=1, + number=2, type=13, cpp_type=3, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='border', full_name='CircleRuleProto.border', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='priority', full_name='CircleRuleProto.priority', index=3, + number=4, type=5, cpp_type=1, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=838, + serialized_end=935, +) + + +_PATHTEXTRULEPROTO = descriptor.Descriptor( + name='PathTextRuleProto', + full_name='PathTextRuleProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='primary', full_name='PathTextRuleProto.primary', index=0, + number=1, type=11, cpp_type=10, label=2, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='secondary', full_name='PathTextRuleProto.secondary', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='priority', full_name='PathTextRuleProto.priority', index=2, + number=3, type=5, cpp_type=1, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=937, + serialized_end=1046, +) + + +_DRAWELEMENTPROTO = descriptor.Descriptor( + name='DrawElementProto', + full_name='DrawElementProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='scale', full_name='DrawElementProto.scale', index=0, + number=1, type=5, cpp_type=1, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='lines', full_name='DrawElementProto.lines', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='area', full_name='DrawElementProto.area', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='symbol', full_name='DrawElementProto.symbol', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='caption', full_name='DrawElementProto.caption', index=4, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='circle', full_name='DrawElementProto.circle', index=5, + number=6, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='path_text', full_name='DrawElementProto.path_text', index=6, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=1049, + serialized_end=1286, +) + + +_CLASSIFELEMENTPROTO = descriptor.Descriptor( + name='ClassifElementProto', + full_name='ClassifElementProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='name', full_name='ClassifElementProto.name', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=unicode("", "utf-8"), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + descriptor.FieldDescriptor( + name='element', full_name='ClassifElementProto.element', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=1288, + serialized_end=1359, +) + + +_CONTAINERPROTO = descriptor.Descriptor( + name='ContainerProto', + full_name='ContainerProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='cont', full_name='ContainerProto.cont', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + serialized_start=1361, + serialized_end=1413, +) + +_LINERULEPROTO.fields_by_name['dashdot'].message_type = _DASHDOTPROTO +_LINERULEPROTO.fields_by_name['pathsym'].message_type = _PATHSYMPROTO +_LINERULEPROTO.fields_by_name['join'].enum_type = _LINEJOIN +_LINERULEPROTO.fields_by_name['cap'].enum_type = _LINECAP +_LINEDEFPROTO.fields_by_name['dashdot'].message_type = _DASHDOTPROTO +_LINEDEFPROTO.fields_by_name['pathsym'].message_type = _PATHSYMPROTO +_LINEDEFPROTO.fields_by_name['join'].enum_type = _LINEJOIN +_LINEDEFPROTO.fields_by_name['cap'].enum_type = _LINECAP +_AREARULEPROTO.fields_by_name['border'].message_type = _LINEDEFPROTO +_CAPTIONRULEPROTO.fields_by_name['primary'].message_type = _CAPTIONDEFPROTO +_CAPTIONRULEPROTO.fields_by_name['secondary'].message_type = _CAPTIONDEFPROTO +_CIRCLERULEPROTO.fields_by_name['border'].message_type = _LINEDEFPROTO +_PATHTEXTRULEPROTO.fields_by_name['primary'].message_type = _CAPTIONDEFPROTO +_PATHTEXTRULEPROTO.fields_by_name['secondary'].message_type = _CAPTIONDEFPROTO +_DRAWELEMENTPROTO.fields_by_name['lines'].message_type = _LINERULEPROTO +_DRAWELEMENTPROTO.fields_by_name['area'].message_type = _AREARULEPROTO +_DRAWELEMENTPROTO.fields_by_name['symbol'].message_type = _SYMBOLRULEPROTO +_DRAWELEMENTPROTO.fields_by_name['caption'].message_type = _CAPTIONRULEPROTO +_DRAWELEMENTPROTO.fields_by_name['circle'].message_type = _CIRCLERULEPROTO +_DRAWELEMENTPROTO.fields_by_name['path_text'].message_type = _PATHTEXTRULEPROTO +_CLASSIFELEMENTPROTO.fields_by_name['element'].message_type = _DRAWELEMENTPROTO +_CONTAINERPROTO.fields_by_name['cont'].message_type = _CLASSIFELEMENTPROTO +DESCRIPTOR.message_types_by_name['DashDotProto'] = _DASHDOTPROTO +DESCRIPTOR.message_types_by_name['PathSymProto'] = _PATHSYMPROTO +DESCRIPTOR.message_types_by_name['LineRuleProto'] = _LINERULEPROTO +DESCRIPTOR.message_types_by_name['LineDefProto'] = _LINEDEFPROTO +DESCRIPTOR.message_types_by_name['AreaRuleProto'] = _AREARULEPROTO +DESCRIPTOR.message_types_by_name['SymbolRuleProto'] = _SYMBOLRULEPROTO +DESCRIPTOR.message_types_by_name['CaptionDefProto'] = _CAPTIONDEFPROTO +DESCRIPTOR.message_types_by_name['CaptionRuleProto'] = _CAPTIONRULEPROTO +DESCRIPTOR.message_types_by_name['CircleRuleProto'] = _CIRCLERULEPROTO +DESCRIPTOR.message_types_by_name['PathTextRuleProto'] = _PATHTEXTRULEPROTO +DESCRIPTOR.message_types_by_name['DrawElementProto'] = _DRAWELEMENTPROTO +DESCRIPTOR.message_types_by_name['ClassifElementProto'] = _CLASSIFELEMENTPROTO +DESCRIPTOR.message_types_by_name['ContainerProto'] = _CONTAINERPROTO + + +class DashDotProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _DASHDOTPROTO + + # @@protoc_insertion_point(class_scope:DashDotProto) + + +class PathSymProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _PATHSYMPROTO + + # @@protoc_insertion_point(class_scope:PathSymProto) + + +class LineRuleProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _LINERULEPROTO + + # @@protoc_insertion_point(class_scope:LineRuleProto) + + +class LineDefProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _LINEDEFPROTO + + # @@protoc_insertion_point(class_scope:LineDefProto) + + +class AreaRuleProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _AREARULEPROTO + + # @@protoc_insertion_point(class_scope:AreaRuleProto) + + +class SymbolRuleProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _SYMBOLRULEPROTO + + # @@protoc_insertion_point(class_scope:SymbolRuleProto) + + +class CaptionDefProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _CAPTIONDEFPROTO + + # @@protoc_insertion_point(class_scope:CaptionDefProto) + + +class CaptionRuleProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _CAPTIONRULEPROTO + + # @@protoc_insertion_point(class_scope:CaptionRuleProto) + + +class CircleRuleProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _CIRCLERULEPROTO + + # @@protoc_insertion_point(class_scope:CircleRuleProto) + + +class PathTextRuleProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _PATHTEXTRULEPROTO + + # @@protoc_insertion_point(class_scope:PathTextRuleProto) + + +class DrawElementProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _DRAWELEMENTPROTO + + # @@protoc_insertion_point(class_scope:DrawElementProto) + + +class ClassifElementProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _CLASSIFELEMENTPROTO + + # @@protoc_insertion_point(class_scope:ClassifElementProto) + + +class ContainerProto(message.Message): + __metaclass__ = reflection.GeneratedProtocolMessageType + DESCRIPTOR = _CONTAINERPROTO + + # @@protoc_insertion_point(class_scope:ContainerProto) + +# @@protoc_insertion_point(module_scope) diff --git a/tools/kothic/libkomwm.py b/tools/kothic/libkomwm.py new file mode 100644 index 0000000000..d14d60f91b --- /dev/null +++ b/tools/kothic/libkomwm.py @@ -0,0 +1,438 @@ +from drules_struct_pb2 import * +from timer import * +from mapcss import MapCSS +from optparse import OptionParser +import os +import csv +import sys +import json +import mapcss.webcolors +whatever_to_hex = mapcss.webcolors.webcolors.whatever_to_hex +whatever_to_cairo = mapcss.webcolors.webcolors.whatever_to_cairo + +WIDTH_SCALE = 1.0 + +def komap_mapswithme(options, style, filename): + if options.outfile == "-": + print "Please specify base output path." + exit() + else: + ddir = os.path.dirname(options.outfile) + + types_file = open(os.path.join(ddir, 'types.txt'), "w") + drules_bin = open(os.path.join(options.outfile + '.bin'), "wb") + drules_txt = open(os.path.join(options.outfile + '.txt'), "wb") + + drules = ContainerProto() + classificator = {} + class_order = [] + class_tree = {} + visibility = {} + textures = {} + + for row in csv.reader(open(os.path.join(ddir, 'mapcss-mapping.csv')), delimiter=';'): + pairs = [i.strip(']').split("=") for i in row[1].split(',')[0].split('[')] + kv = {} + for i in pairs: + if len(i) == 1: + if i[0]: + if i[0][0] == "!": + kv[i[0][1:].strip('?')] = "no" + else: + kv[i[0].strip('?')] = "yes" + else: + kv[i[0]] = i[1] + classificator[row[0].replace("|", "-")] = kv + if row[2] != "x": + class_order.append(row[0].replace("|", "-")) + print >> types_file, row[0] + else: + # compatibility mode + if row[6]: + print >> types_file, row[6] + else: + print >> types_file, "mapswithme" + class_tree[row[0].replace("|", "-")] = row[0] + class_order.sort() + types_file.close() + + def mwm_encode_color(st, prefix='', default='black'): + if prefix: + prefix += "-" + opacity = hex(255 - int(255 * float(st.get(prefix + "opacity", 1)))) + color = whatever_to_hex(st.get(prefix + 'color', default)) + color = color[1] + color[1] + color[3] + color[3] + color[5] + color[5] + return int(opacity + color, 16) + + def mwm_encode_image(st, prefix='icon', bgprefix='symbol'): + if prefix: + prefix += "-" + if bgprefix: + bgprefix += "-" + if prefix + "image" not in st: + return False + # strip last ".svg" + handle = st.get(prefix + "image")[:-4] + return handle, handle + + bgpos = 0 + + dr_linecaps = {'none': BUTTCAP, 'butt': BUTTCAP, 'round': ROUNDCAP} + dr_linejoins = {'none': NOJOIN, 'bevel': BEVELJOIN, 'round': ROUNDJOIN} + + # atbuild = AccumulativeTimer() + # atzstyles = AccumulativeTimer() + # atdrcont = AccumulativeTimer() + # atline = AccumulativeTimer() + # atarea = AccumulativeTimer() + # atnode = AccumulativeTimer() + + # atbuild.Start() + + for cl in class_order: + clname = cl if cl.find('-') == -1 else cl[:cl.find('-')] + # clname = cl + style.build_choosers_tree(clname, "line", classificator[cl]) + style.build_choosers_tree(clname, "area", classificator[cl]) + style.build_choosers_tree(clname, "node", classificator[cl]) + + style.restore_choosers_order("line"); + style.restore_choosers_order("area"); + style.restore_choosers_order("node"); + + # atbuild.Stop() + + for cl in class_order: + visstring = ["0"] * (options.maxzoom - options.minzoom + 1) + + clname = cl if cl.find('-') == -1 else cl[:cl.find('-')] + # clname = cl + txclass = classificator[cl] + txclass["name"] = "name" + txclass["addr:housenumber"] = "addr:housenumber" + txclass["ref"] = "ref" + txclass["int_name"] = "int_name" + txclass["addr:flats"] = "addr:flats" + + prev_area_len = -1 + prev_node_len = -1 + prev_line_len = -1 + check_area = True + check_node = True + check_line = True + + # atzstyles.Start() + + zstyles_arr = [None] * (options.maxzoom - options.minzoom + 1) + has_icons_for_areas_arr = [False] * (options.maxzoom - options.minzoom + 1) + + for zoom in xrange(options.maxzoom, options.minzoom - 1, -1): + has_icons_for_areas = False + zstyle = {} + + if check_line: + if "area" not in txclass: + # atline.Start() + linestyle = style.get_style_dict(clname, "line", txclass, zoom, olddict=zstyle, cache=False) + if prev_line_len == -1: + prev_line_len = len(linestyle) + if len(linestyle) == 0: + if prev_line_len != 0: + check_line = False + zstyle = linestyle + # atline.Stop() + + if check_area: + # atarea.Start() + areastyle = style.get_style_dict(clname, "area", txclass, zoom, olddict=zstyle, cache=False) + for st in areastyle.values(): + if "icon-image" in st or 'symbol-shape' in st or 'symbol-image' in st: + has_icons_for_areas = True + break + if prev_area_len == -1: + prev_area_len = len(areastyle) + if len(areastyle) == 0: + if prev_area_len != 0: + check_area = False + zstyle = areastyle + # atarea.Stop() + + if check_node: + if "area" not in txclass: + # atnode.Start() + nodestyle = style.get_style_dict(clname, "node", txclass, zoom, olddict=zstyle, cache=False) + if prev_node_len == -1: + prev_node_len = len(nodestyle) + if len(nodestyle) == 0: + if prev_node_len != 0: + check_node = False + zstyle = nodestyle + # atnode.Stop() + + if not check_line and not check_area and not check_node: + break + + zstyle = zstyle.values() + + zstyles_arr[zoom - options.minzoom] = zstyle + has_icons_for_areas_arr[zoom - options.minzoom]= has_icons_for_areas + + # atzstyles.Stop() + + # atdrcont.Start() + + dr_cont = ClassifElementProto() + dr_cont.name = cl + for zoom in xrange(options.minzoom, options.maxzoom + 1): + zstyle = zstyles_arr[zoom - options.minzoom] + if zstyle is None or len(zstyle) == 0: + continue + has_icons_for_areas = has_icons_for_areas_arr[zoom - options.minzoom] + + has_lines = False + has_icons = False + has_fills = False + for st in zstyle: + st = dict([(k, v) for k, v in st.iteritems() if str(v).strip(" 0.")]) + if 'width' in st or 'pattern-image' in st: + has_lines = True + if 'icon-image' in st or 'symbol-shape' in st or 'symbol-image' in st: + has_icons = True + if 'fill-color' in st: + has_fills = True + + has_text = None + txfmt = [] + for st in zstyle: + if st.get('text') and not st.get('text') in txfmt: + txfmt.append(st.get('text')) + if has_text is None: + has_text = [] + has_text.append(st) + + if has_lines or has_text or has_fills or has_icons: + visstring[zoom] = "1" + dr_element = DrawElementProto() + dr_element.scale = zoom + + for st in zstyle: + if st.get('-x-kot-layer') == 'top': + st['z-index'] = float(st.get('z-index', 0)) + 15001. + elif st.get('-x-kot-layer') == 'bottom': + st['z-index'] = float(st.get('z-index', 0)) - 15001. + + if st.get('casing-width') not in (None, 0): # and (st.get('width') or st.get('fill-color')): + if st.get('casing-linecap', 'butt') == 'butt': + dr_line = LineRuleProto() + dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + (st.get('casing-width') * WIDTH_SCALE * 2) + dr_line.color = mwm_encode_color(st, "casing") + dr_line.priority = min(int(st.get('z-index', 0) + 999), 20000) + dashes = st.get('casing-dashes', st.get('dashes', [])) + dr_line.dashdot.dd.extend(dashes) + dr_line.cap = dr_linecaps.get(st.get('casing-linecap', 'butt'), BUTTCAP) + dr_line.join = dr_linejoins.get(st.get('casing-linejoin', 'round'), ROUNDJOIN) + dr_element.lines.extend([dr_line]) + + # Let's try without this additional line style overhead. Needed only for casing in road endings. + # if st.get('casing-linecap', st.get('linecap', 'round')) != 'butt': + # dr_line = LineRuleProto() + # dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + (st.get('casing-width') * WIDTH_SCALE * 2) + # dr_line.color = mwm_encode_color(st, "casing") + # dr_line.priority = -15000 + # dashes = st.get('casing-dashes', st.get('dashes', [])) + # dr_line.dashdot.dd.extend(dashes) + # dr_line.cap = dr_linecaps.get(st.get('casing-linecap', 'round'), ROUNDCAP) + # dr_line.join = dr_linejoins.get(st.get('casing-linejoin', 'round'), ROUNDJOIN) + # dr_element.lines.extend([dr_line]) + + if has_lines: + if st.get('width'): + dr_line = LineRuleProto() + dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + dr_line.color = mwm_encode_color(st) + for i in st.get('dashes', []): + dr_line.dashdot.dd.extend([max(float(i), 1) * WIDTH_SCALE]) + dr_line.cap = dr_linecaps.get(st.get('linecap', 'butt'), BUTTCAP) + dr_line.join = dr_linejoins.get(st.get('linejoin', 'round'), ROUNDJOIN) + dr_line.priority = min((int(st.get('z-index', 0)) + 1000), 20000) + dr_element.lines.extend([dr_line]) + if st.get('pattern-image'): + dr_line = LineRuleProto() + dr_line.width = 0 + dr_line.color = 0 + icon = mwm_encode_image(st, prefix='pattern') + dr_line.pathsym.name = icon[0] + dr_line.pathsym.step = float(st.get('pattern-spacing', 0)) - 16 + dr_line.pathsym.offset = st.get('pattern-offset', 0) + dr_line.priority = int(st.get('z-index', 0)) + 1000 + dr_element.lines.extend([dr_line]) + textures[icon[0]] = icon[1] + + if has_icons: + if st.get('icon-image'): + if not has_icons_for_areas: + dr_element.symbol.apply_for_type = 1 + icon = mwm_encode_image(st) + dr_element.symbol.name = icon[0] + dr_element.symbol.priority = min(19100, (16000 + int(st.get('z-index', 0)))) + textures[icon[0]] = icon[1] + has_icons = False + if st.get('symbol-shape'): + dr_element.circle.radius = float(st.get('symbol-size')) + dr_element.circle.color = mwm_encode_color(st, 'symbol-fill') + dr_element.circle.priority = min(19000, (14000 + int(st.get('z-index', 0)))) + has_icons = False + + if has_text and st.get('text'): + has_text = has_text[:2] + has_text.reverse() + dr_text = dr_element.path_text + base_z = 15000 + if st.get('text-position', 'center') == 'line': + dr_text = dr_element.path_text + base_z = 16000 + else: + dr_text = dr_element.caption + for sp in has_text[:]: + dr_cur_subtext = dr_text.primary + if len(has_text) == 2: + dr_cur_subtext = dr_text.secondary + dr_cur_subtext.height = int(float(sp.get('font-size', "10").split(",")[0])) + dr_cur_subtext.color = mwm_encode_color(sp, "text") + if st.get('text-halo-radius', 0) != 0: + dr_cur_subtext.stroke_color = mwm_encode_color(sp, "text-halo", "white") + if 'text-offset' in sp or 'text-offset-y' in sp: + dr_cur_subtext.offset_y = int(sp.get('text-offset-y', sp.get('text-offset', 0))) + if 'text-offset-x' in sp: + dr_cur_subtext.offset_x = int(sp.get('text-offset-x', 0)) + has_text.pop() + dr_text.priority = min(19000, (base_z + int(st.get('z-index', 0)))) + has_text = None + + if has_fills: + if ('fill-color' in st) and (float(st.get('fill-opacity', 1)) > 0): + dr_element.area.color = mwm_encode_color(st, "fill") + if st.get('fill-position', 'foreground') == 'background': + if 'z-index' not in st: + bgpos -= 1 + dr_element.area.priority = bgpos - 16000 + else: + zzz = int(st.get('z-index', 0)) + if zzz > 0: + dr_element.area.priority = zzz - 16000 + else: + dr_element.area.priority = zzz - 16700 + else: + dr_element.area.priority = (int(st.get('z-index', 0)) + 1 + 1000) + has_fills = False + + dr_cont.element.extend([dr_element]) + + if dr_cont.element: + drules.cont.extend([dr_cont]) + + # atdrcont.Stop() + + visibility["world|" + class_tree[cl] + "|"] = "".join(visstring) + + # atwrite = AccumulativeTimer() + # atwrite.Start() + + drules_bin.write(drules.SerializeToString()) + drules_txt.write(unicode(drules)) + + visnodes = set() + for k, v in visibility.iteritems(): + vis = k.split("|") + for i in range(1, len(vis) - 1): + visnodes.add("|".join(vis[0:i]) + "|") + viskeys = list(set(visibility.keys() + list(visnodes))) + + def cmprepl(a, b): + if a == b: + return 0 + a = a.replace("|", "-") + b = b.replace("|", "-") + if a > b: + return 1 + return -1 + viskeys.sort(cmprepl) + + visibility_file = open(os.path.join(ddir, 'visibility.txt'), "w") + classificator_file = open(os.path.join(ddir, 'classificator.txt'), "w") + + oldoffset = "" + for k in viskeys: + offset = " " * (k.count("|") - 1) + for i in range(len(oldoffset) / 4, len(offset) / 4, -1): + print >> visibility_file, " " * i + "{}" + print >> classificator_file, " " * i + "{}" + + oldoffset = offset + end = "-" + if k in visnodes: + end = "+" + print >> visibility_file, offset + k.split("|")[-2] + " " + visibility.get(k, "0" * (options.maxzoom + 1)) + " " + end + print >> classificator_file, offset + k.split("|")[-2] + " " + end + for i in range(len(offset) / 4, 0, -1): + print >> visibility_file, " " * i + "{}" + print >> classificator_file, " " * i + "{}" + + # atwrite.Stop() + + # print "build, sec: %s" % (atbuild.ElapsedSec()) + # print "zstyle %s times, sec: %s" % (atzstyles.Count(), atzstyles.ElapsedSec()) + # print "drcont %s times, sec: %s" % (atdrcont.Count(), atdrcont.ElapsedSec()) + # print "line %s times, sec: %s" % (atline.Count(), atline.ElapsedSec()) + # print "area %s times, sec: %s" % (atarea.Count(), atarea.ElapsedSec()) + # print "node %s times, sec: %s" % (atnode.Count(), atnode.ElapsedSec()) + # print "writing files, sec: %s" % (atwrite.ElapsedSec()) + +# Main + +parser = OptionParser() +parser.add_option("-s", "--stylesheet", dest="filename", + help="read MapCSS stylesheet from FILE", metavar="FILE") +parser.add_option("-f", "--minzoom", dest="minzoom", default=0, type="int", + help="minimal available zoom level", metavar="ZOOM") +parser.add_option("-t", "--maxzoom", dest="maxzoom", default=19, type="int", + help="maximal available zoom level", metavar="ZOOM") +parser.add_option("-l", "--locale", dest="locale", + help="language that should be used for labels (ru, en, be, uk..)", metavar="LANG") +parser.add_option("-o", "--output-file", dest="outfile", default="-", + help="output filename (defaults to stdout)", metavar="FILE") +parser.add_option("-p", "--osm2pgsql-style", dest="osm2pgsqlstyle", default="-", + help="osm2pgsql stylesheet filename", metavar="FILE") +parser.add_option("-b", "--background-only", dest="bgonly", action="store_true", default=False, + help="Skip rendering of icons and labels", metavar="BOOL") +parser.add_option("-T", "--text-scale", dest="textscale", default=1, type="float", + help="text size scale", metavar="SCALE") +parser.add_option("-c", "--config", dest="conffile", default="komap.conf", + help="config file name", metavar="FILE") + +(options, args) = parser.parse_args() + +if (options.filename is None): + parser.error("MapCSS stylesheet filename is required") + +try: + # atparse = AccumulativeTimer() + # atbuild = AccumulativeTimer() + + # atparse.Start() + style = MapCSS(options.minzoom, options.maxzoom + 1) # zoom levels + style.parse(filename = options.filename) + # atparse.Stop() + + # atbuild.Start() + komap_mapswithme(options, style, options.filename) + # atbuild.Stop() + + # print "mapcss parse, sec: %s" % (atparse.ElapsedSec()) + # print "build, sec: %s" % (atbuild.ElapsedSec()) + + exit(0) + +except Exception as e: + print >> sys.stderr, "Error\n" + str(e) + exit(-1) diff --git a/tools/kothic/mapcss/Condition.py b/tools/kothic/mapcss/Condition.py new file mode 100644 index 0000000000..a476eb9379 --- /dev/null +++ b/tools/kothic/mapcss/Condition.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# This file is part of kothic, the realtime map renderer. + +# kothic is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# kothic 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 General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with kothic. If not, see . + +import re + +INVERSIONS = {"eq": "ne", "true": "false", "set": "unset", "<": ">=", ">": "<="} +in2 = {} +for a, b in INVERSIONS.iteritems(): + in2[b] = a +INVERSIONS.update(in2) +del in2 + +# Fast conditions + +class EqConditionDD: + def __init__(self, params): + self.value = params[1] + def extract_tags(self): + return set(["*"]) + def test(self, tags): + return self.value + +class EqCondition: + def __init__(self, params): + self.tag = params[0] + self.value = params[1] + def extract_tags(self): + return set([self.tag]) + def test(self, tags): + if self.tag in tags: + return tags[self.tag] == self.value + else: + return False + +class NotEqCondition: + def __init__(self, params): + self.tag = params[0] + self.value = params[1] + def extract_tags(self): + return set([self.tag]) + def test(self, tags): + if self.tag in tags: + return tags[self.tag] != self.value + else: + return False + +class SetCondition: + def __init__(self, params): + self.tag = params[0] + def extract_tags(self): + return set([self.tag]) + def test(self, tags): + if self.tag in tags: + return tags[self.tag] != '' + return False + +class UnsetCondition: + def __init__(self, params): + self.tag = params[0] + def extract_tags(self): + return set([self.tag]) + def test(self, tags): + if self.tag in tags: + return tags[self.tag] == '' + return True + +class TrueCondition: + def __init__(self, params): + self.tag = params[0] + def extract_tags(self): + return set([self.tag]) + def test(self, tags): + if self.tag in tags: + return tags[self.tag] == 'yes' + return False + +class UntrueCondition: + def __init__(self, params): + self.tag = params[0] + def extract_tags(self): + return set([self.tag]) + def test(self, tags): + if self.tag in tags: + return tags[self.tag] == 'no' + return False + +# Slow condition + +class Condition: + def __init__(self, typez, params): + self.type = typez # eq, regex, lt, gt etc. + if type(params) == type(str()): + params = (params,) + self.params = params # e.g. ('highway','primary') + if typez == "regex": + self.regex = re.compile(self.params[0], re.I) + self.compiled_regex = "" + + def get_interesting_tags(self): + if self.params[0][:2] == "::": + return [] + return set([self.params[0]]) + + def extract_tags(self): + if self.params[0][:2] == "::" or self.type == "regex": + return set(["*"]) # unknown + return set([self.params[0]]) + + def get_numerics(self): + if self.type in ("<", ">", ">=", "<="): + return self.params[0] + else: + return False + + def test(self, tags): + """ + Test a hash against this condition + """ + t = self.type + params = self.params + if t == 'eq': # don't compare tags against sublayers + if params[0][:2] == "::": + return params[1] + try: + if t == 'eq': + return tags[params[0]] == params[1] + if t == 'ne': + return tags.get(params[0], "") != params[1] + if t == 'regex': + return bool(self.regex.match(tags[params[0]])) + if t == 'true': + return tags.get(params[0]) == 'yes' + if t == 'untrue': + return tags.get(params[0]) == 'no' + if t == 'set': + if params[0] in tags: + return tags[params[0]] != '' + return False + if t == 'unset': + if params[0] in tags: + return tags[params[0]] == '' + return True + + if t == '<': + return (Number(tags[params[0]]) < Number(params[1])) + if t == '<=': + return (Number(tags[params[0]]) <= Number(params[1])) + if t == '>': + return (Number(tags[params[0]]) > Number(params[1])) + if t == '>=': + return (Number(tags[params[0]]) >= Number(params[1])) + except KeyError: + pass + return False + + def inverse(self): + """ + Get a not-A for condition A + """ + t = self.type + params = self.params + try: + return Condition(INVERSIONS[t], params) + if t == 'regex': + ### FIXME: learn how to invert regexes + return Condition("regex", params) + except KeyError: + pass + return self + + def get_sql(self): + # params = [re.escape(x) for x in self.params] + params = self.params + t = self.type + if t == 'eq': # don't compare tags against sublayers + if params[0][:2] == "::": + return ("", "") + try: + if t == 'eq': + return params[0], '"%s" = \'%s\'' % (params[0], params[1]) + if t == 'ne': + return params[0], '("%s" != \'%s\' or "%s" IS NULL)' % (params[0], params[1], params[0]) + if t == 'regex': + return params[0], '"%s" ~ \'%s\'' % (params[0], params[1].replace("'", "\\'")) + if t == 'true': + return params[0], '"%s" = \'yes\'' % (params[0]) + if t == 'untrue': + return params[0], '"%s" = \'no\'' % (params[0]) + if t == 'set': + return params[0], '"%s" IS NOT NULL' % (params[0]) + if t == 'unset': + return params[0], '"%s" IS NULL' % (params[0]) + + if t == '<': + return params[0], """(CASE WHEN "%s" ~ E'^[-]?[[:digit:]]+([.][[:digit:]]+)?$' THEN CAST ("%s" AS FLOAT) < %s ELSE false END) """ % (params[0], params[0], params[1]) + if t == '<=': + return params[0], """(CASE WHEN "%s" ~ E'^[-]?[[:digit:]]+([.][[:digit:]]+)?$' THEN CAST ("%s" AS FLOAT) <= %s ELSE false END)""" % (params[0], params[0], params[1]) + if t == '>': + return params[0], """(CASE WHEN "%s" ~ E'^[-]?[[:digit:]]+([.][[:digit:]]+)?$' THEN CAST ("%s" AS FLOAT) > %s ELSE false END) """ % (params[0], params[0], params[1]) + if t == '>=': + return params[0], """(CASE WHEN "%s" ~ E'^[-]?[[:digit:]]+([.][[:digit:]]+)?$' THEN CAST ("%s" AS FLOAT) >= %s ELSE false END) """ % (params[0], params[0], params[1]) + except KeyError: + pass + + def get_mapnik_filter(self): + # params = [re.escape(x) for x in self.params] + params = self.params + t = self.type + if t == 'eq': # don't compare tags against sublayers + if params[0][:2] == "::": + return '' + try: + if t == 'eq': + return '[%s] = \'%s\'' % (params[0], params[1]) + if t == 'ne': + return 'not([%s] = \'%s\')' % (params[0], params[1]) + if t == 'regex': + return '[%s].match(\'%s\')' % (params[0], params[1].replace("'", "\\'")) + if t == 'true': + return '[%s] = \'yes\'' % (params[0]) + if t == 'untrue': + return '[%s] = \'no\'' % (params[0]) + if t == 'set': + return '[%s] != \'\'' % (params[0]) + if t == 'unset': + return 'not([%s] != \'\')' % (params[0]) + + if t == '<': + return '[%s__num] < %s' % (params[0], float(params[1])) + if t == '<=': + return '[%s__num] <= %s' % (params[0], float(params[1])) + if t == '>': + return '[%s__num] > %s' % (params[0], float(params[1])) + if t == '>=': + return '[%s__num] >= %s' % (params[0], float(params[1])) + # return "" + except KeyError: + pass + + def __repr__(self): + return "%s %s " % (self.type, repr(self.params)) + + def __eq__(self, a): + return (self.params == a.params) and (self.type == a.type) + + def and_with(self, c2): + """ + merges two rules with AND. + """ + # TODO: possible other minimizations + if c2.params[0] == self.params[0]: + if c2.params == self.params: + if c2.type == INVERSIONS[self.type]: # for example, eq AND ne = 0 + return False + if c2.type == self.type: + return (self,) + + if self.type == ">=" and c2.type == "<=": # a<=2 and a>=2 --> a=2 + return (Condition("eq", self.params),) + if self.type == "<=" and c2.type == ">=": + return (Condition("eq", self.params),) + if self.type == ">" and c2.type == "<": + return False + if self.type == "<" and c2.type == ">": + return False + + if c2.type == "eq" and self.type in ("ne", "<", ">"): + if c2.params[1] != self.params[1]: + return (c2,) + if self.type == "eq" and c2.type in ("ne", "<", ">"): + if c2.params[1] != self.params[1]: + return (self,) + if self.type == "eq" and c2.type == "eq": + if c2.params[1] != self.params[1]: + return False + if c2.type == "set" and self.type in ("eq", "ne", "regex", "<", "<=", ">", ">="): # a is set and a == b -> a == b + return (self,) + if c2.type == "unset" and self.type in ("eq", "ne", "regex", "<", "<=", ">", ">="): # a is unset and a == b -> impossible + return False + if self.type == "set" and c2.type in ("eq", "ne", "regex", "<", "<=", ">", ">="): + return (c2,) + if self.type == "unset" and c2.type in ("eq", "ne", "regex", "<", "<=", ">", ">="): + return False + + return self, c2 + +def Number(tt): + """ + Wrap float() not to produce exceptions + """ + try: + return float(tt) + except ValueError: + return 0 + +# Some conditions we can optimize by using "python polymorthism" + +def OptimizeCondition(condition): + if (condition.type == "eq"): + if (condition.params[0][:2] == "::"): + return EqConditionDD(condition.params) + else: + return EqCondition(condition.params) + elif (condition.type == "ne"): + return NotEqCondition(condition.params) + elif (condition.type == "set"): + return SetCondition(condition.params) + elif (condition.type == "unset"): + return UnsetCondition(condition.params) + elif (condition.type == "true"): + return TrueCondition(condition.params) + elif (condition.type == "untrue"): + return UntrueCondition(condition.params) + else: + return condition diff --git a/tools/kothic/mapcss/Eval.py b/tools/kothic/mapcss/Eval.py new file mode 100644 index 0000000000..3c32832d07 --- /dev/null +++ b/tools/kothic/mapcss/Eval.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# This file is part of kothic, the realtime map renderer. + +# kothic is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# kothic 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 General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with kothic. If not, see . + +NONE = "" + + +class Eval(): + def __init__(self, s='eval()'): + """ + Parse expression and convert it into Python + """ + s = s.strip()[5:-1].strip() + self.expr_text = s + try: + self.expr = compile(s, "MapCSS expression", "eval") + except: + # print "Can't compile %s" % s + self.expr = compile("0", "MapCSS expression", "eval") + + def extract_tags(self): + """ + Extracts list of tags that might be used in calculation + """ + def fake_compute(*x): + """ + Perform a fake computation. Always computes all the parameters, always returns 0. + WARNING: Might not cope with complex statements. + """ + for t in x: + q = x + return 0 + + # print self.expr_text + tags = set([]) + a = eval(self.expr, {}, { + "tag": lambda x: max([tags.add(x), 0]), + "prop": lambda x: 0, + "num": lambda x: 0, + "metric": fake_compute, + "zmetric": fake_compute, + "str": lambda x: "", + "any": fake_compute, + "min": fake_compute, + "max": fake_compute, + }) + return tags + + def compute(self, tags={}, props={}, xscale=1., zscale=0.5): + """ + Compute this eval() + """ + """ + for k, v in tags.iteritems(): + try: + tags[k] = float(v) + except: + pass + """ + try: + return str(eval(self.expr, {}, { + "tag": lambda x: tags.get(x, ""), + "prop": lambda x: props.get(x, ""), + "num": m_num, + "metric": lambda x: m_metric(x, xscale), + "zmetric": lambda x: m_metric(x, zscale), + "str": str, + "any": m_any, + "min": m_min, + "max": m_max, + "cond": m_cond, + "boolean": m_boolean + })) + except: + return "" + + def __repr__(self): + return "eval(%s)" % self.expr_text + + +def m_boolean(expr): + expr = str(expr) + if expr in ("", "0", "no", "false", "False"): + return False + else: + return True + + +def m_cond(why, yes, no): + if m_boolean(why): + return yes + else: + return no + + +def m_min(*x): + """ + min() MapCSS Feature + """ + try: + return min([m_num(t) for t in x]) + except: + return 0 + + +def m_max(*x): + """ + max() MapCSS Feature + """ + try: + return max([m_num(t) for t in x]) + except: + return 0 + + +def m_any(*x): + """ + any() MapCSS feature + """ + for t in x: + if t: + return t + else: + return "" + + +def m_num(x): + """ + num() MapCSS feature + """ + try: + return float(str(x)) + except ValueError: + return 0 + + +def m_metric(x, t): + """ + metric() and zmetric() function. + """ + x = str(x) + try: + return float(x) * float(t) + except: + "Heuristics." + # FIXME: add ft, m and friends + x = x.strip() + try: + if x[-2:] in ("cm", "CM", "см"): + return float(x[0:-2]) * float(t) / 100 + if x[-2:] in ("mm", "MM", "мм"): + return float(x[0:-2]) * float(t) / 1000 + if x[-1] in ("m", "M", "м"): + return float(x[0:-1]) * float(t) + except: + return "" + + +# def str(x): + #""" + # str() MapCSS feature + #""" + # return __builtins__.str(x) + + +if __name__ == "__main__": + a = Eval(""" eval( any( metric(tag("height")), metric ( num(tag("building:levels")) * 3), metric("1m"))) """) + print repr(a) + print a.compute({"building:levels": "3"}) + print a.extract_tags() diff --git a/tools/kothic/mapcss/Rule.py b/tools/kothic/mapcss/Rule.py new file mode 100644 index 0000000000..e2858d86c0 --- /dev/null +++ b/tools/kothic/mapcss/Rule.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# This file is part of kothic, the realtime map renderer. + +# kothic is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# kothic 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 General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with kothic. If not, see . + +type_matches = { + "": ('area', 'line', 'way', 'node'), + "area": ("area", "way"), + "node": ("node",), + "way": ("line", "area", "way"), + "line": ("line", "area"), + } + +class Rule(): + def __init__(self, s=''): + self.conditions = [] + # self.isAnd = True + self.minZoom = 0 + self.maxZoom = 19 + if s == "*": + s = "" + self.subject = s # "", "way", "node" or "relation" + + def __repr__(self): + return "%s|z%s-%s %s" % (self.subject, self.minZoom, self.maxZoom, self.conditions) + + def test(self, obj, tags, zoom): + if (zoom < self.minZoom) or (zoom > self.maxZoom): + return False + + if (self.subject != '') and not _test_feature_compatibility(obj, self.subject, tags): + return False + + subpart = "::default" + for condition in self.conditions: + res = condition.test(tags) + if not res: + return False + if type(res) != bool: + subpart = res + return subpart + + def test_zoom(self, zoom): + return (zoom >= self.minZoom) and (zoom <= self.maxZoom) + + def get_compatible_types(self): + return type_matches.get(self.subject, (self.subject,)) + + def get_interesting_tags(self, obj, zoom): + if obj: + if (self.subject != '') and not _test_feature_compatibility(obj, self.subject, {}): + return set() + + if zoom and not self.test_zoom(zoom): + return set() + + a = set() + for condition in self.conditions: + a.update(condition.get_interesting_tags()) + return a + + def extract_tags(self): + a = set() + for condition in self.conditions: + a.update(condition.extract_tags()) + if "*" in a: + break + return a + + def get_numerics(self): + a = set() + for condition in self.conditions: + a.add(condition.get_numerics()) + a.discard(False) + return a + + def get_sql_hints(self, obj, zoom): + if obj: + if (self.subject != '') and not _test_feature_compatibility(obj, self.subject, {":area": "yes"}): + return set() + if not self.test_zoom(zoom): + return set() + a = set() + b = set() + for condition in self.conditions: + q = condition.get_sql() + if q: + if q[1]: + a.add(q[0]) + b.add(q[1]) + b = " AND ".join(b) + return a, b + + +def _test_feature_compatibility(f1, f2, tags={}): + """ + Checks if feature of type f1 is compatible with f2. + """ + if f2 == f1: + return True + if f2 not in ("way", "area", "line"): + return False + elif f2 == "way" and f1 == "line": + return True + elif f2 == "way" and f1 == "area": + return True + elif f2 == "area" and f1 in ("way", "area"): +# if ":area" in tags: + return True +# else: +# return False + elif f2 == "line" and f1 in ("way", "line", "area"): + return True + else: + return False + # print f1, f2, True + return True diff --git a/tools/kothic/mapcss/StyleChooser.py b/tools/kothic/mapcss/StyleChooser.py new file mode 100644 index 0000000000..b2cfb723cc --- /dev/null +++ b/tools/kothic/mapcss/StyleChooser.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# This file is part of kothic, the realtime map renderer. + +# kothic is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# kothic 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 General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with kothic. If not, see . + + +from Rule import Rule +from webcolors.webcolors import whatever_to_cairo as colorparser +from webcolors.webcolors import cairo_to_hex +from Eval import Eval +from Condition import * + +TYPE_EVAL = type(Eval()) + +def make_nice_style(r): + ra = {} + for a, b in r.iteritems(): + "checking and nicifying style table" + if type(b) == TYPE_EVAL: + ra[a] = b + elif "color" in a: + "parsing color value to 3-tuple" + # print "res:", b + if b and (type(b) != tuple): + # if not b: + # print sl, ftype, tags, zoom, scale, zscale + # else: + ra[a] = colorparser(b) + elif b: + ra[a] = b + elif any(x in a for x in ("width", "z-index", "opacity", "offset", "radius", "extrude")): + "these things are float's or not in table at all" + try: + ra[a] = float(b) + except ValueError: + pass + elif "dashes" in a and type(b) != list: + "these things are arrays of float's or not in table at all" + try: + b = b.split(",") + b = [float(x) for x in b] + ra[a] = b + except ValueError: + ra[a] = [] + else: + ra[a] = b + return ra + + +class StyleChooser: + """ + A StyleChooser object is equivalent to one CSS selector+declaration. + + Its ruleChains property is an array of all the selectors, which would + traditionally be comma-separated. For example: + h1, h2, h3 em + is three ruleChains. + + Each ruleChain is itself an array of nested selectors. So the above + example would roughly be encoded as: + [[h1],[h2],[h3,em]] + ^^ ^^ ^^ ^^ each of these is a Rule + + The styles property is an array of all the style objects to be drawn + if any of the ruleChains evaluate to true. + """ + def __repr__(self): + return "{(%s) : [%s] }\n" % (self.ruleChains, self.styles) + + def __init__(self, scalepair): + self.ruleChains = [] + self.styles = [] + self.eval_type = TYPE_EVAL + self.scalepair = scalepair + self.selzooms = None + self.compatible_types = set() + self.has_evals = False + + def get_numerics(self): + """ + Returns a set of number-compared values. + """ + a = set() + for r in self.ruleChains: + a.update(r.get_numerics()) + a.discard(False) + return a + + def get_interesting_tags(self, ztype, zoom): + """ + Returns a set of tags that were used in here. + """ + ### FIXME + a = set() + for r in self.ruleChains: + a.update(r.get_interesting_tags(ztype, zoom)) + if a: # FIXME: semi-illegal optimization, may wreck in future on tagless matches + for r in self.styles: + for c, b in r.iteritems(): + if type(b) == self.eval_type: + a.update(b.extract_tags()) + return a + + def extract_tags(self): + a = set() + for r in self.ruleChains: + a.update(r.extract_tags()) + if "*" in a: + a.clear() + a.add("*") + break + if self.has_evals and "*" not in a: + for s in self.styles: + for v in s.values(): + if type(v) == self.eval_type: + a.update(v.extract_tags()) + if "*" in a or len(a) == 0: + a.clear() + a.add("*") + return a + + def get_sql_hints(self, type, zoom): + """ + Returns a set of tags that were used in here in form of SQL-hints. + """ + a = set() + b = "" + needed = set(["width", "casing-width", "fill-color", "fill-image", "icon-image", "text", "extrude", "background-image", "background-color", "pattern-image", "shield-text"]) + + if not needed.isdisjoint(set(self.styles[0].keys())): + for r in self.ruleChains: + p = r.get_sql_hints(type, zoom) + if p: + q = "(" + p[1] + ")" # [t[1] for t in p] + if q == "()": + q = "" + if b and q: + b += " OR " + q + else: + b = q + a.update(p[0]) + # no need to check for eval's + return a, b + + def updateStyles(self, sl, ftype, tags, zoom, scale, zscale): + # Are any of the ruleChains fulfilled? + if self.selzooms: + if zoom < self.selzooms[0] or zoom > self.selzooms[1]: + return sl + + #if ftype not in self.compatible_types: +# return sl + + object_id = self.testChain(self.ruleChains, ftype, tags, zoom) + + if not object_id: + return sl + + w = 0 + + for r in self.styles: + if self.has_evals: + ra = {} + for a, b in r.iteritems(): + "calculating eval()'s" + if type(b) == self.eval_type: + combined_style = {} + for t in sl: + combined_style.update(t) + for p, q in combined_style.iteritems(): + if "color" in p: + combined_style[p] = cairo_to_hex(q) + b = b.compute(tags, combined_style, scale, zscale) + ra[a] = b + #r = ra + ra = make_nice_style(ra) + else: + ra = r.copy() + + ra["object-id"] = str(object_id) + hasall = False + allinit = {} + for x in sl: + if x.get("object-id") == "::*": + allinit = x.copy() + if ra["object-id"] == "::*": + oid = x.get("object-id") + x.update(ra) + x["object-id"] = oid + if oid == "::*": + hasall = True + else: + if x.get("object-id") == ra["object-id"]: + x.update(ra) + break + else: + if not hasall: + allinit.update(ra) + sl.append(allinit) + + return sl + + def testChain(self, chain, obj, tags, zoom): + """ + Tests an object against a chain + """ + for r in chain: + tt = r.test(obj, tags, zoom) + if tt: + return tt + return False + + def newGroup(self): + """ + starts a new ruleChain in this.ruleChains + """ + pass + + def newObject(self, e=''): + # print "newRule" + """ + adds into the current ruleChain (starting a new Rule) + """ + rule = Rule(e) + rule.minZoom = float(self.scalepair[0]) + rule.maxZoom = float(self.scalepair[1]) + self.ruleChains.append(rule) + + def addZoom(self, z): + # print "addZoom ", float(z[0]), ", ", float(z[1]) + """ + adds into the current ruleChain (existing Rule) + """ + self.ruleChains[-1].minZoom = float(z[0]) + self.ruleChains[-1].maxZoom = float(z[1]) + + def addCondition(self, c): + # print "addCondition ", c + """ + adds into the current ruleChain (existing Rule) + """ + c = OptimizeCondition(c) + self.ruleChains[-1].conditions.append(c) + + def addStyles(self, a): + # print "addStyle ", a + """ + adds to this.styles + """ + for r in self.ruleChains: + if not self.selzooms: + self.selzooms = [r.minZoom, r.maxZoom] + else: + self.selzooms[0] = min(self.selzooms[0], r.minZoom) + self.selzooms[1] = max(self.selzooms[1], r.maxZoom) + self.compatible_types.update(r.get_compatible_types()) + rb = [] + for r in a: + ra = {} + for a, b in r.iteritems(): + a = a.strip() + b = b.strip() + if a == "casing-width": + "josm support" + if b[0] == "+": + try: + b = str(float(b) / 2) + except: + pass + if "text" == a[-4:]: + if b[:5] != "eval(": + b = "eval(tag(\"" + b + "\"))" + if b[:5] == "eval(": + b = Eval(b) + self.has_evals = True + ra[a] = b + ra = make_nice_style(ra) + rb.append(ra) + self.styles = self.styles + rb diff --git a/tools/kothic/mapcss/__init__.py b/tools/kothic/mapcss/__init__.py new file mode 100644 index 0000000000..427c9dba33 --- /dev/null +++ b/tools/kothic/mapcss/__init__.py @@ -0,0 +1,471 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# This file is part of kothic, the realtime map renderer. + +# kothic is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# kothic 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 General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with kothic. If not, see . + +import re +import os +import logging +from hashlib import md5 + +from copy import copy, deepcopy + + +from StyleChooser import StyleChooser +from Condition import Condition + + +NEEDED_KEYS = set(["width", "casing-width", "fill-color", "fill-image", "icon-image", "text", "extrude", + "background-image", "background-color", "pattern-image", "shield-text", "symbol-shape"]) + + +WHITESPACE = re.compile(r'^ \s+ ', re.S | re.X) + +COMMENT = re.compile(r'^ \/\* .+? \*\/ \s* ', re.S | re.X) +CLASS = re.compile(r'^ ([\.:]:?[*\w]+) \s* ', re.S | re.X) +#NOT_CLASS = re.compile(r'^ !([\.:]\w+) \s* ', re.S | re.X) +ZOOM = re.compile(r'^ \| \s* z([\d\-]+) \s* ', re.I | re.S | re.X) +GROUP = re.compile(r'^ , \s* ', re.I | re.S | re.X) +CONDITION = re.compile(r'^ \[(.+?)\] \s* ', re.S | re.X) +OBJECT = re.compile(r'^ (\*|[\w]+) \s* ', re.S | re.X) +DECLARATION = re.compile(r'^ \{(.+?)\} \s* ', re.S | re.X) +IMPORT = re.compile(r'^@import\("(.+?)"\); \s* ', re.S | re.X) +VARIABLE_SET = re.compile(r'^@([a-z][\w\d]*) \s* : \s* (.+?) \s* ; \s* ', re.S | re.X | re.I) +UNKNOWN = re.compile(r'^ (\S+) \s* ', re.S | re.X) + +ZOOM_MINMAX = re.compile(r'^ (\d+)\-(\d+) $', re.S | re.X) +ZOOM_MIN = re.compile(r'^ (\d+)\- $', re.S | re.X) +ZOOM_MAX = re.compile(r'^ \-(\d+) $', re.S | re.X) +ZOOM_SINGLE = re.compile(r'^ (\d+) $', re.S | re.X) + +CONDITION_TRUE = re.compile(r'^ \s* ([:\w]+) \s* [?] \s* $', re.I | re.S | re.X) +CONDITION_invTRUE = re.compile(r'^ \s* [!] \s* ([:\w]+) \s* [?] \s* $', re.I | re.S | re.X) +CONDITION_FALSE = re.compile(r'^ \s* ([:\w]+) \s* = \s* no \s* $', re.I | re.S | re.X) +CONDITION_SET = re.compile(r'^ \s* ([-:\w]+) \s* $', re.S | re.X) +CONDITION_UNSET = re.compile(r'^ \s* !([:\w]+) \s* $', re.S | re.X) +CONDITION_EQ = re.compile(r'^ \s* ([:\w]+) \s* = \s* (.+) \s* $', re.S | re.X) +CONDITION_NE = re.compile(r'^ \s* ([:\w]+) \s* != \s* (.+) \s* $', re.S | re.X) +CONDITION_GT = re.compile(r'^ \s* ([:\w]+) \s* > \s* (.+) \s* $', re.S | re.X) +CONDITION_GE = re.compile(r'^ \s* ([:\w]+) \s* >= \s* (.+) \s* $', re.S | re.X) +CONDITION_LT = re.compile(r'^ \s* ([:\w]+) \s* < \s* (.+) \s* $', re.S | re.X) +CONDITION_LE = re.compile(r'^ \s* ([:\w]+) \s* <= \s* (.+) \s* $', re.S | re.X) +CONDITION_REGEX = re.compile(r'^ \s* ([:\w]+) \s* =~\/ \s* (.+) \/ \s* $', re.S | re.X) + +ASSIGNMENT_EVAL = re.compile(r"^ \s* (\S+) \s* \: \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $", re.I | re.S | re.X) +ASSIGNMENT = re.compile(r'^ \s* (\S+) \s* \: \s* (.+?) \s* $', re.S | re.X) +SET_TAG_EVAL = re.compile(r"^ \s* set \s+(\S+)\s* = \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $", re.I | re.S | re.X) +SET_TAG = re.compile(r'^ \s* set \s+(\S+)\s* = \s* (.+?) \s* $', re.I | re.S | re.X) +SET_TAG_TRUE = re.compile(r'^ \s* set \s+(\S+)\s* $', re.I | re.S | re.X) +EXIT = re.compile(r'^ \s* exit \s* $', re.I | re.S | re.X) + +oZOOM = 2 +oGROUP = 3 +oCONDITION = 4 +oOBJECT = 5 +oDECLARATION = 6 +oSUBPART = 7 +oVARIABLE_SET = 8 + +DASH = re.compile(r'\-/g') +COLOR = re.compile(r'color$/') +BOLD = re.compile(r'^bold$/i') +ITALIC = re.compile(r'^italic|oblique$/i') +UNDERLINE = re.compile(r'^underline$/i') +CAPS = re.compile(r'^uppercase$/i') +CENTER = re.compile(r'^center$/i') + +HEX = re.compile(r'^#([0-9a-f]+)$/i') +VARIABLE = re.compile(r'@([a-z][\w\d]*)') + + +class MapCSS(): + def __init__(self, minscale=0, maxscale=19): + """ + """ + self.cache = {} + self.cache["style"] = {} + self.minscale = minscale + self.maxscale = maxscale + self.scalepair = (minscale, maxscale) + self.choosers = [] + self.choosers_by_type = {} + self.choosers_by_type_and_tag = {} + self.variables = {} + self.style_loaded = False + + def parseZoom(self, s): + if ZOOM_MINMAX.match(s): + return tuple([float(i) for i in ZOOM_MINMAX.match(s).groups()]) + elif ZOOM_MIN.match(s): + return float(ZOOM_MIN.match(s).groups()[0]), self.maxscale + elif ZOOM_MAX.match(s): + return float(self.minscale), float(ZOOM_MAX.match(s).groups()[0]) + elif ZOOM_SINGLE.match(s): + return float(ZOOM_SINGLE.match(s).groups()[0]), float(ZOOM_SINGLE.match(s).groups()[0]) + else: + logging.error("unparsed zoom: %s" % s) + + def build_choosers_tree(self, clname, type, tags={}): + if type not in self.choosers_by_type_and_tag: + self.choosers_by_type_and_tag[type] = {} + if clname not in self.choosers_by_type_and_tag[type]: + self.choosers_by_type_and_tag[type][clname] = set() + if type in self.choosers_by_type: + for chooser in self.choosers_by_type[type]: + for tag in chooser.extract_tags(): + if tag == "*" or tag in tags: + if chooser not in self.choosers_by_type_and_tag[type][clname]: + self.choosers_by_type_and_tag[type][clname].add(chooser) + break + + def restore_choosers_order(self, type): + ethalon_choosers = self.choosers_by_type[type] + for tag, choosers_for_tag in self.choosers_by_type_and_tag[type].items(): + tmp = [] + for ec in ethalon_choosers: + if ec in choosers_for_tag: + tmp.append(ec) + self.choosers_by_type_and_tag[type][tag] = tmp + + def get_style(self, clname, type, tags={}, zoom=0, scale=1, zscale=.5, cache=True): + """ + Kothic styling API + """ + if cache: + shash = md5(repr(type) + repr(tags) + repr(zoom)).digest() + if shash in self.cache["style"]: + return deepcopy(self.cache["style"][shash]) + style = [] + if type in self.choosers_by_type_and_tag: + choosers = self.choosers_by_type_and_tag[type][clname] + for chooser in choosers: + style = chooser.updateStyles(style, type, tags, zoom, scale, zscale) + style = [x for x in style if x["object-id"] != "::*"] + for x in style: + for k, v in [('width', 0), ('casing-width', 0)]: + if k in x: + if x[k] == v: + del x[k] + st = [] + for x in style: + if not NEEDED_KEYS.isdisjoint(x): + st.append(x) + style = st + + if cache: + self.cache["style"][shash] = deepcopy(style) + return style + + def get_style_dict(self, clname, type, tags={}, zoom=0, scale=1, zscale=.5, olddict={}, cache=True): + r = self.get_style(clname, type, tags, zoom, scale, zscale, cache) + d = olddict + for x in r: + if x.get('object-id', '') not in d: + d[x.get('object-id', '')] = {} + d[x.get('object-id', '')].update(x) + return d + + def get_interesting_tags(self, type=None, zoom=None): + """ + Get set of interesting tags. + """ + tags = set() + for chooser in self.choosers: + tags.update(chooser.get_interesting_tags(type, zoom)) + return tags + + def get_sql_hints(self, type=None, zoom=None): + """ + Get set of interesting tags. + """ + hints = [] + for chooser in self.choosers: + p = chooser.get_sql_hints(type, zoom) + if p: + if p[0] and p[1]: + hints.append(p) + return hints + + def subst_variables(self, t): + """Expects an array from parseDeclaration.""" + for k in t[0]: + t[0][k] = VARIABLE.sub(self.get_variable, t[0][k]) + return t + + def get_variable(self, m): + name = m.group()[1:] + if not name in self.variables: + raise Exception("Variable not found: " + str(format(name))) + return self.variables[name] if name in self.variables else m.group() + + def parse(self, css=None, clamp=True, stretch=1000, filename=None): + """ + Parses MapCSS given as string + """ + basepath = os.curdir + if filename: + basepath = os.path.dirname(filename) + if not css: + css = open(filename).read() + if not self.style_loaded: + self.choosers = [] + + log = logging.getLogger('mapcss.parser') + previous = 0 # what was the previous CSS word? + sc = StyleChooser(self.scalepair) # currently being assembled + + stck = [] # filename, original, remained + stck.append([filename, css, css]) + try: + while (len(stck) > 0): + css = stck[-1][1].lstrip() # remained + + wasBroken = False + while (css): + # Class - :motorway, :builtup, :hover + if CLASS.match(css): + if previous == oDECLARATION: + self.choosers.append(sc) + sc = StyleChooser(self.scalepair) + cond = CLASS.match(css).groups()[0] + log.debug("class found: %s" % (cond)) + css = CLASS.sub("", css) + sc.addCondition(Condition('eq', ("::class", cond))) + previous = oCONDITION + + ## Not class - !.motorway, !.builtup, !:hover + #elif NOT_CLASS.match(css): + #if (previous == oDECLARATION): + #self.choosers.append(sc) + #sc = StyleChooser(self.scalepair) + #cond = NOT_CLASS.match(css).groups()[0] + #log.debug("not_class found: %s" % (cond)) + #css = NOT_CLASS.sub("", css) + #sc.addCondition(Condition('ne', ("::class", cond))) + #previous = oCONDITION + + # Zoom + elif ZOOM.match(css): + if (previous != oOBJECT & previous != oCONDITION): + sc.newObject() + cond = ZOOM.match(css).groups()[0] + log.debug("zoom found: %s" % (cond)) + css = ZOOM.sub("", css) + sc.addZoom(self.parseZoom(cond)) + previous = oZOOM + + # Grouping - just a comma + elif GROUP.match(css): + css = GROUP.sub("", css) + sc.newGroup() + previous = oGROUP + + # Condition - [highway=primary] + elif CONDITION.match(css): + if (previous == oDECLARATION): + self.choosers.append(sc) + sc = StyleChooser(self.scalepair) + if (previous != oOBJECT) and (previous != oZOOM) and (previous != oCONDITION): + sc.newObject() + cond = CONDITION.match(css).groups()[0] + log.debug("condition found: %s" % (cond)) + css = CONDITION.sub("", css) + sc.addCondition(parseCondition(cond)) + previous = oCONDITION + + # Object - way, node, relation + elif OBJECT.match(css): + if (previous == oDECLARATION): + self.choosers.append(sc) + sc = StyleChooser(self.scalepair) + obj = OBJECT.match(css).groups()[0] + log.debug("object found: %s" % (obj)) + css = OBJECT.sub("", css) + sc.newObject(obj) + previous = oOBJECT + + # Declaration - {...} + elif DECLARATION.match(css): + decl = DECLARATION.match(css).groups()[0] + log.debug("declaration found: %s" % (decl)) + sc.addStyles(self.subst_variables(parseDeclaration(decl))) + css = DECLARATION.sub("", css) + previous = oDECLARATION + + # CSS comment + elif COMMENT.match(css): + log.debug("comment found") + css = COMMENT.sub("", css) + + # @import("filename.css"); + elif IMPORT.match(css): + log.debug("import found") + import_filename = os.path.join(basepath, IMPORT.match(css).groups()[0]) + try: + css = IMPORT.sub("", css) + import_text = open(import_filename, "r").read() + stck[-1][1] = css # store remained part + stck.append([import_filename, import_text, import_text]) + wasBroken = True + break + except IOError as e: + raise Exception("Cannot import file " + import_filename + "\n" + str(e)) + + # Variables + elif VARIABLE_SET.match(css): + name = VARIABLE_SET.match(css).groups()[0] + log.debug("variable set found: %s" % name) + self.variables[name] = VARIABLE_SET.match(css).groups()[1] + css = VARIABLE_SET.sub("", css) + previous = oVARIABLE_SET + + # Unknown pattern + elif UNKNOWN.match(css): + raise Exception("Unknown construction: " + UNKNOWN.match(css).group()) + + # Must be unreacheable + else: + raise Exception("Unexpected construction: " + css) + + if not wasBroken: + stck.pop() + + if (previous == oDECLARATION): + self.choosers.append(sc) + sc = StyleChooser(self.scalepair) + + except Exception as e: + filename = stck[-1][0] # filename + css_orig = stck[-1][2] # original + css = stck[-1][1] # remained + line = unicode(css_orig[:-len(unicode(css))]).count("\n") + 1 + msg = str(e) + "\nFile: " + filename + "\nLine: " + str(line) + raise Exception(msg) + + try: + if clamp: + "clamp z-indexes, so they're tightly following integers" + zindex = set() + for chooser in self.choosers: + for stylez in chooser.styles: + zindex.add(float(stylez.get('z-index', 0))) + zindex = list(zindex) + zindex.sort() + zoffset = len([x for x in zindex if x < 0]) + for chooser in self.choosers: + for stylez in chooser.styles: + if 'z-index' in stylez: + res = zindex.index(float(stylez.get('z-index', 0))) - zoffset + if stretch: + stylez['z-index'] = 1. * res / len(zindex) * stretch + else: + stylez['z-index'] = res + except TypeError: + pass + + for chooser in self.choosers: + for t in chooser.compatible_types: + if t not in self.choosers_by_type: + self.choosers_by_type[t] = [chooser] + else: + self.choosers_by_type[t].append(chooser) + + +def parseCondition(s): + log = logging.getLogger('mapcss.parser.condition') + + if CONDITION_TRUE.match(s): + a = CONDITION_TRUE.match(s).groups() + log.debug("condition true: %s" % (a[0])) + return Condition('true', a) + + if CONDITION_invTRUE.match(s): + a = CONDITION_invTRUE.match(s).groups() + log.debug("condition invtrue: %s" % (a[0])) + return Condition('ne', (a[0], "yes")) + + if CONDITION_FALSE.match(s): + a = CONDITION_FALSE.match(s).groups() + log.debug("condition false: %s" % (a[0])) + return Condition('false', a) + + if CONDITION_SET.match(s): + a = CONDITION_SET.match(s).groups() + log.debug("condition set: %s" % (a)) + return Condition('set', a) + + if CONDITION_UNSET.match(s): + a = CONDITION_UNSET.match(s).groups() + log.debug("condition unset: %s" % (a)) + return Condition('unset', a) + + if CONDITION_NE.match(s): + a = CONDITION_NE.match(s).groups() + log.debug("condition NE: %s = %s" % (a[0], a[1])) + return Condition('ne', a) + + if CONDITION_LE.match(s): + a = CONDITION_LE.match(s).groups() + log.debug("condition LE: %s <= %s" % (a[0], a[1])) + return Condition('<=', a) + + if CONDITION_GE.match(s): + a = CONDITION_GE.match(s).groups() + log.debug("condition GE: %s >= %s" % (a[0], a[1])) + return Condition('>=', a) + + if CONDITION_LT.match(s): + a = CONDITION_LT.match(s).groups() + log.debug("condition LT: %s < %s" % (a[0], a[1])) + return Condition('<', a) + + if CONDITION_GT.match(s): + a = CONDITION_GT.match(s).groups() + log.debug("condition GT: %s > %s" % (a[0], a[1])) + return Condition('>', a) + + if CONDITION_REGEX.match(s): + a = CONDITION_REGEX.match(s).groups() + log.debug("condition REGEX: %s = %s" % (a[0], a[1])) + return Condition('regex', a) + + if CONDITION_EQ.match(s): + a = CONDITION_EQ.match(s).groups() + log.debug("condition EQ: %s = %s" % (a[0], a[1])) + return Condition('eq', a) + + else: + raise Exception("condition UNKNOWN: " + s) + + +def parseDeclaration(s): + """ + Parse declaration string into list of styles + """ + t = {} + for a in s.split(';'): + # if ((o=ASSIGNMENT_EVAL.exec(a))) { t[o[1].replace(DASH,'_')]=new Eval(o[2]); } + if ASSIGNMENT.match(a): + tzz = ASSIGNMENT.match(a).groups() + t[tzz[0]] = tzz[1].strip().strip('"') + logging.debug("%s == %s" % (tzz[0], tzz[1])) + else: + logging.debug("unknown %s" % (a)) + return [t] + + +if __name__ == "__main__": + logging.basicConfig(level=logging.WARNING) + mc = MapCSS(0, 19) diff --git a/tools/kothic/mapcss/webcolors/LICENSE.txt b/tools/kothic/mapcss/webcolors/LICENSE.txt new file mode 100644 index 0000000000..61deabe362 --- /dev/null +++ b/tools/kothic/mapcss/webcolors/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright (c) 2008-2009, James Bennett +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the author nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tools/kothic/mapcss/webcolors/PKG-INFO b/tools/kothic/mapcss/webcolors/PKG-INFO new file mode 100644 index 0000000000..baab7fc15c --- /dev/null +++ b/tools/kothic/mapcss/webcolors/PKG-INFO @@ -0,0 +1,439 @@ +Metadata-Version: 1.0 +Name: webcolors +Version: 1.3 +Summary: A library for working with sRGB color specifications as used in HTML and CSS. +Home-page: http://www.bitbucket.org/ubernostrum/webcolors/overview/ +Author: James Bennett +Author-email: james@b-list.org +License: UNKNOWN +Download-URL: http://bitbucket.org/ubernostrum/webcolors/downloads/webcolors-1.3.tar.gz +Description: Web colors + ========== + + A simple library for working with the color names and color codes + defined by the HTML and CSS specifications. + + + An overview of HTML and CSS colors + ---------------------------------- + + Colors on the Web are specified in `the sRGB color space`_, where each + color is made up of a red component, a green component and a blue + component. This is useful because it maps (fairly) cleanly to the red, + green and blue components of pixels on a computer display, and to the + cone cells of a human eye, which come in three sets roughly + corresponding to the wavelengths of light associated with red, green + and blue. + + `The HTML 4 standard`_ defines two ways to specify sRGB colors: + + * A hash mark ('#') followed by three pairs of hexdecimal digits, + specifying values for red, green and blue components in that order; + for example, ``#0099cc``. Since each pair of hexadecimal digits can + express 256 different values, this allows up to 256**3 or 16,777,216 + unique colors to be specified (though, due to differences in display + technology, not all of these colors may be clearly distinguished on + any given physical display). + + * A set of predefined color names which correspond to specific + hexadecimal values; for example, ``white``. HTML 4 defines sixteen + such colors. + + `The CSS 2 standard`_ allows any valid HTML 4 color specification, and + adds three new ways to specify sRGB colors: + + * A hash mark followed by three hexadecimal digits, which is expanded + into three hexadecimal pairs by repeating each digit; thus ``#09c`` + is equivalent to ``#0099cc``. + + * The string 'rgb', followed by parentheses, between which are three + numeric values each between 0 and 255, inclusive, which are taken to + be the values of the red, green and blue components in that order; + for example, ``rgb(0, 153, 204)``. + + * The same as above, except using percentages instead of numeric + values; for example, ``rgb(0%, 60%, 80%)``. + + `The CSS 2.1 revision`_ does not add any new methods of specifying + sRGB colors, but does add one additional named color. + + `The CSS 3 color module`_ (currently a W3C Candidate Recommendation) + adds one new way to specify sRGB colors: + + * A hue-saturation-lightness triple (HSL), using the construct + ``hsl()``. + + It also adds support for variable opacity of colors, by allowing the + specification of alpha-channel information, through the ``rgba()`` and + ``hsla()`` constructs, which are identical to ``rgb()`` and ``hsl()`` + with one exception: a fourth value is supplied, indicating the level + of opacity from ``0.0`` (completely transparent) to ``1.0`` + (completely opaque). Though not technically a color, the keyword + ``transparent`` is also made available in lieu of a color value, and + corresponds to ``rgba(0,0,0,0)``. + + Additionally, CSS3 defines a new set of color names; this set is taken + directly from the named colors defined for SVG (Scalable Vector + Graphics) markup, and is a proper superset of the named colors defined + in CSS 2.1. This set also has significant overlap with traditional X11 + color sets as defined by the ``rgb.txt`` file on many Unix and + Unix-like operating systems, though the correspondence is not exact; + the set of X11 colors is not standardized, and the set of CSS3 colors + contains some definitions which diverge significantly from customary + X11 definitions (for example, CSS3's ``green`` is not equivalent to + X11's ``green``; the value which X11 designates ``green`` is + designated ``lime`` in CSS3). + + .. _the sRGB color space: http://www.w3.org/Graphics/Color/sRGB + .. _The HTML 4 standard: http://www.w3.org/TR/html401/types.html#h-6.5 + .. _The CSS 2 standard: http://www.w3.org/TR/REC-CSS2/syndata.html#value-def-color + .. _The CSS 2.1 revision: http://www.w3.org/TR/CSS21/ + .. _The CSS 3 color module: http://www.w3.org/TR/css3-color/ + + What this module supports + ------------------------- + + The mappings and functions within this module support the following + methods of specifying sRGB colors, and conversions between them: + + * Six-digit hexadecimal. + + * Three-digit hexadecimal. + + * Integer ``rgb()`` triplet. + + * Percentage ``rgb()`` triplet. + + * Varying selections of predefined color names (see below). + + This module does not support ``hsl()`` triplets, nor does it support + opacity/alpha-channel information via ``rgba()`` or ``hsla()``. + + If you need to convert between RGB-specified colors and HSL-specified + colors, or colors specified via other means, consult `the colorsys + module`_ in the Python standard library, which can perform conversions + amongst several common color spaces. + + .. _the colorsys module: http://docs.python.org/library/colorsys.html + + Normalization + ------------- + + For colors specified via hexadecimal values, this module will accept + input in the following formats: + + * A hash mark (#) followed by three hexadecimal digits, where letters + may be upper- or lower-case. + + * A hash mark (#) followed by six hexadecimal digits, where letters + may be upper- or lower-case. + + For output which consists of a color specified via hexadecimal values, + and for functions which perform intermediate conversion to hexadecimal + before returning a result in another format, this module always + normalizes such values to the following format: + + * A hash mark (#) followed by six hexadecimal digits, with letters + forced to lower-case. + + The function ``normalize_hex()`` in this module can be used to perform + this normalization manually if desired; see its documentation for an + explanation of the normalization process. + + For colors specified via predefined names, this module will accept + input in the following formats: + + * An entirely lower-case name, such as ``aliceblue``. + + * A name using initial capitals, such as ``AliceBlue``. + + For output which consists of a color specified via a predefined name, + and for functions which perform intermediate conversion to a + predefined name before returning a result in another format, this + module always normalizes such values to be entirely lower-case. + + Mappings of color names + ----------------------- + + For each set of defined color names -- HTML 4, CSS 2, CSS 2.1 and CSS + 3 -- this module exports two mappings: one of normalized color names + to normalized hexadecimal values, and one of normalized hexadecimal + values to normalized color names. These eight mappings are as follows: + + ``html4_names_to_hex`` + Mapping of normalized HTML 4 color names to normalized hexadecimal + values. + + ``html4_hex_to_names`` + Mapping of normalized hexadecimal values to normalized HTML 4 + color names. + + ``css2_names_to_hex`` + Mapping of normalized CSS 2 color names to normalized hexadecimal + values. Because CSS 2 defines the same set of named colors as HTML + 4, this is merely an alias for ``html4_names_to_hex``. + + ``css2_hex_to_names`` + Mapping of normalized hexadecimal values to normalized CSS 2 color + nams. For the reasons described above, this is merely an alias for + ``html4_hex_to_names``. + + ``css21_names_to_hex`` + Mapping of normalized CSS 2.1 color names to normalized + hexadecimal values. This is identical to ``html4_names_to_hex``, + except for one addition: ``orange``. + + ``css21_hex_to_names`` + Mapping of normalized hexadecimal values to normalized CSS 2.1 + color names. As above, this is identical to ``html4_hex_to_names`` + except for the addition of ``orange``. + + ``css3_names_to_hex`` + Mapping of normalized CSS3 color names to normalized hexadecimal + values. + + ``css3_hex_to_names`` + Mapping of normalized hexadecimal values to normalized CSS3 color + names. + + Normalization functions + ----------------------- + + ``normalize_hex(hex_value)`` + Normalize a hexadecimal color value to the following form and + return the result:: + + #[a-f0-9]{6} + + In other words, the following transformations are applied as + needed: + + * If the value contains only three hexadecimal digits, it is + expanded to six. + + * The value is normalized to lower-case. + + If the supplied value cannot be interpreted as a hexadecimal color + value, ``ValueError`` is raised. + + Example: + + >>> normalize_hex('#09c') + '#0099cc' + + Conversions from named colors + ----------------------------- + + ``name_to_hex(name, spec='css3')`` + Convert a color name to a normalized hexadecimal color value. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The color name will be normalized to lower-case before being + looked up, and when no color of that name exists in the given + specification, ``ValueError`` is raised. + + Example: + + >>> name_to_hex('deepskyblue') + '#00bfff' + + ``name_to_rgb(name, spec='css3')`` + Convert a color name to a 3-tuple of integers suitable for use in + an ``rgb()`` triplet specifying that color. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The color name will be normalized to lower-case before being + looked up, and when no color of that name exists in the given + specification, ``ValueError`` is raised. + + Example: + + >>> name_to_rgb('navy') + (0, 0, 128) + + ``name_to_rgb_percent(name, spec='css3')`` + Convert a color name to a 3-tuple of percentages suitable for use + in an ``rgb()`` triplet specifying that color. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The color name will be normalized to lower-case before being + looked up, and when no color of that name exists in the given + specification, ``ValueError`` is raised. + + Example: + + >>> name_to_rgb_percent('navy') + ('0%', '0%', '50%') + + + Conversions from hexadecimal values + ----------------------------------- + + ``hex_to_name(hex_value, spec='css3')`` + Convert a hexadecimal color value to its corresponding normalized + color name, if any such name exists. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The hexadecimal value will be normalized before being looked up, + and when no color name for the value is found in the given + specification, ``ValueError`` is raised. + + Example: + + >>> hex_to_name('#000080') + 'navy' + + ``hex_to_rgb(hex_value)`` + Convert a hexadecimal color value to a 3-tuple of integers + suitable for use in an ``rgb()`` triplet specifying that color. + + The hexadecimal value will be normalized before being converted. + + Example: + + >>> hex_to_rgb('#000080') + (0, 0, 128) + + ``hex_to_rgb_percent(hex_value)`` + Convert a hexadecimal color value to a 3-tuple of percentages + suitable for use in an ``rgb()`` triplet representing that color. + + The hexadecimal value will be normalized before converting. + + Example: + + >>> hex_to_rgb_percent('#ffffff') + ('100%', '100%', '100%') + + + Conversions from integer rgb() triplets + --------------------------------------- + + ``rgb_to_name(rgb_triplet, spec='css3')`` + Convert a 3-tuple of integers, suitable for use in an ``rgb()`` + color triplet, to its corresponding normalized color name, if any + such name exists. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + If there is no matching name, ``ValueError`` is raised. + + Example: + + >>> rgb_to_name((0, 0, 0)) + 'black' + + ``rgb_to_hex(rgb_triplet)`` + Convert a 3-tuple of integers, suitable for use in an ``rgb()`` + color triplet, to a normalized hexadecimal value for that color. + + Example: + + >>> rgb_to_hex((255, 255, 255)) + '#ffffff' + + ``rgb_to_rgb_percent(rgb_triplet)`` + Convert a 3-tuple of integers, suitable for use in an ``rgb()`` + color triplet, to a 3-tuple of percentages suitable for use in + representing that color. + + This function makes some trade-offs in terms of the accuracy of + the final representation; for some common integer values, + special-case logic is used to ensure a precise result (e.g., + integer 128 will always convert to '50%', integer 32 will always + convert to '12.5%'), but for all other values a standard Python + ``float`` is used and rounded to two decimal places, which may + result in a loss of precision for some values. + + Examples: + + >>> rgb_to_rgb_percent((255, 255, 255)) + ('100%', '100%', '100%') + >>> rgb_to_rgb_percent((0, 0, 128)) + ('0%', '0%', '50%') + >>> rgb_to_rgb_percent((33, 56, 192)) + ('12.94%', '21.96%', '75.29%') + >>> rgb_to_rgb_percent((64, 32, 16)) + ('25%', '12.5%', '6.25%') + + + Conversions from percentage rgb() triplets + ------------------------------------------ + + ``rgb_percent_to_name(rgb_percent_triplet, spec='css3')`` + Convert a 3-tuple of percentages, suitable for use in an ``rgb()`` + color triplet, to its corresponding normalized color name, if any + such name exists. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + If there is no matching name, ``ValueError`` is raised. + + Example: + + >>> rgb_percent_to_name(('0%', '0%', '50%')) + 'navy' + + ``rgb_percent_to_hex(rgb_percent_triplet)`` + Convert a 3-tuple of percentages, suitable for use in an ``rgb()`` + color triplet, to a normalized hexadecimal color value for that + color. + + Example: + + >>> rgb_percent_to_hex(('100%', '100%', '0%')) + '#ffff00' + + ``rgb_percent_to_rgb(rgb_percent_triplet)`` + Convert a 3-tuple of percentages, suitable for use in an ``rgb()`` + color triplet, to a 3-tuple of integers suitable for use in + representing that color. + + Some precision may be lost in this conversion. See the note + regarding precision for ``rgb_to_rgb_percent()`` for details; + generally speaking, the following is true for any 3-tuple ``t`` of + integers in the range 0...255 inclusive:: + + t == rgb_percent_to_rgb(rgb_to_rgb_percent(t)) + + Examples: + + >>> rgb_percent_to_rgb(('100%', '100%', '100%')) + (255, 255, 255) + >>> rgb_percent_to_rgb(('0%', '0%', '50%')) + (0, 0, 128) + >>> rgb_percent_to_rgb(('25%', '12.5%', '6.25%')) + (64, 32, 16) + >>> rgb_percent_to_rgb(('12.94%', '21.96%', '75.29%')) + (33, 56, 192) + +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Utilities diff --git a/tools/kothic/mapcss/webcolors/README.txt b/tools/kothic/mapcss/webcolors/README.txt new file mode 100644 index 0000000000..4d95ae8cd6 --- /dev/null +++ b/tools/kothic/mapcss/webcolors/README.txt @@ -0,0 +1,421 @@ +Web colors +========== + +A simple library for working with the color names and color codes +defined by the HTML and CSS specifications. + + +An overview of HTML and CSS colors +---------------------------------- + +Colors on the Web are specified in `the sRGB color space`_, where each +color is made up of a red component, a green component and a blue +component. This is useful because it maps (fairly) cleanly to the red, +green and blue components of pixels on a computer display, and to the +cone cells of a human eye, which come in three sets roughly +corresponding to the wavelengths of light associated with red, green +and blue. + +`The HTML 4 standard`_ defines two ways to specify sRGB colors: + +* A hash mark ('#') followed by three pairs of hexdecimal digits, + specifying values for red, green and blue components in that order; + for example, ``#0099cc``. Since each pair of hexadecimal digits can + express 256 different values, this allows up to 256**3 or 16,777,216 + unique colors to be specified (though, due to differences in display + technology, not all of these colors may be clearly distinguished on + any given physical display). + +* A set of predefined color names which correspond to specific + hexadecimal values; for example, ``white``. HTML 4 defines sixteen + such colors. + +`The CSS 2 standard`_ allows any valid HTML 4 color specification, and +adds three new ways to specify sRGB colors: + +* A hash mark followed by three hexadecimal digits, which is expanded + into three hexadecimal pairs by repeating each digit; thus ``#09c`` + is equivalent to ``#0099cc``. + +* The string 'rgb', followed by parentheses, between which are three + numeric values each between 0 and 255, inclusive, which are taken to + be the values of the red, green and blue components in that order; + for example, ``rgb(0, 153, 204)``. + +* The same as above, except using percentages instead of numeric + values; for example, ``rgb(0%, 60%, 80%)``. + +`The CSS 2.1 revision`_ does not add any new methods of specifying +sRGB colors, but does add one additional named color. + +`The CSS 3 color module`_ (currently a W3C Candidate Recommendation) +adds one new way to specify sRGB colors: + +* A hue-saturation-lightness triple (HSL), using the construct + ``hsl()``. + +It also adds support for variable opacity of colors, by allowing the +specification of alpha-channel information, through the ``rgba()`` and +``hsla()`` constructs, which are identical to ``rgb()`` and ``hsl()`` +with one exception: a fourth value is supplied, indicating the level +of opacity from ``0.0`` (completely transparent) to ``1.0`` +(completely opaque). Though not technically a color, the keyword +``transparent`` is also made available in lieu of a color value, and +corresponds to ``rgba(0,0,0,0)``. + +Additionally, CSS3 defines a new set of color names; this set is taken +directly from the named colors defined for SVG (Scalable Vector +Graphics) markup, and is a proper superset of the named colors defined +in CSS 2.1. This set also has significant overlap with traditional X11 +color sets as defined by the ``rgb.txt`` file on many Unix and +Unix-like operating systems, though the correspondence is not exact; +the set of X11 colors is not standardized, and the set of CSS3 colors +contains some definitions which diverge significantly from customary +X11 definitions (for example, CSS3's ``green`` is not equivalent to +X11's ``green``; the value which X11 designates ``green`` is +designated ``lime`` in CSS3). + +.. _the sRGB color space: http://www.w3.org/Graphics/Color/sRGB +.. _The HTML 4 standard: http://www.w3.org/TR/html401/types.html#h-6.5 +.. _The CSS 2 standard: http://www.w3.org/TR/REC-CSS2/syndata.html#value-def-color +.. _The CSS 2.1 revision: http://www.w3.org/TR/CSS21/ +.. _The CSS 3 color module: http://www.w3.org/TR/css3-color/ + +What this module supports +------------------------- + +The mappings and functions within this module support the following +methods of specifying sRGB colors, and conversions between them: + +* Six-digit hexadecimal. + +* Three-digit hexadecimal. + +* Integer ``rgb()`` triplet. + +* Percentage ``rgb()`` triplet. + +* Varying selections of predefined color names (see below). + +This module does not support ``hsl()`` triplets, nor does it support +opacity/alpha-channel information via ``rgba()`` or ``hsla()``. + +If you need to convert between RGB-specified colors and HSL-specified +colors, or colors specified via other means, consult `the colorsys +module`_ in the Python standard library, which can perform conversions +amongst several common color spaces. + +.. _the colorsys module: http://docs.python.org/library/colorsys.html + +Normalization +------------- + +For colors specified via hexadecimal values, this module will accept +input in the following formats: + +* A hash mark (#) followed by three hexadecimal digits, where letters + may be upper- or lower-case. + +* A hash mark (#) followed by six hexadecimal digits, where letters + may be upper- or lower-case. + +For output which consists of a color specified via hexadecimal values, +and for functions which perform intermediate conversion to hexadecimal +before returning a result in another format, this module always +normalizes such values to the following format: + +* A hash mark (#) followed by six hexadecimal digits, with letters + forced to lower-case. + +The function ``normalize_hex()`` in this module can be used to perform +this normalization manually if desired; see its documentation for an +explanation of the normalization process. + +For colors specified via predefined names, this module will accept +input in the following formats: + +* An entirely lower-case name, such as ``aliceblue``. + +* A name using initial capitals, such as ``AliceBlue``. + +For output which consists of a color specified via a predefined name, +and for functions which perform intermediate conversion to a +predefined name before returning a result in another format, this +module always normalizes such values to be entirely lower-case. + +Mappings of color names +----------------------- + +For each set of defined color names -- HTML 4, CSS 2, CSS 2.1 and CSS +3 -- this module exports two mappings: one of normalized color names +to normalized hexadecimal values, and one of normalized hexadecimal +values to normalized color names. These eight mappings are as follows: + +``html4_names_to_hex`` + Mapping of normalized HTML 4 color names to normalized hexadecimal + values. + +``html4_hex_to_names`` + Mapping of normalized hexadecimal values to normalized HTML 4 + color names. + +``css2_names_to_hex`` + Mapping of normalized CSS 2 color names to normalized hexadecimal + values. Because CSS 2 defines the same set of named colors as HTML + 4, this is merely an alias for ``html4_names_to_hex``. + +``css2_hex_to_names`` + Mapping of normalized hexadecimal values to normalized CSS 2 color + nams. For the reasons described above, this is merely an alias for + ``html4_hex_to_names``. + +``css21_names_to_hex`` + Mapping of normalized CSS 2.1 color names to normalized + hexadecimal values. This is identical to ``html4_names_to_hex``, + except for one addition: ``orange``. + +``css21_hex_to_names`` + Mapping of normalized hexadecimal values to normalized CSS 2.1 + color names. As above, this is identical to ``html4_hex_to_names`` + except for the addition of ``orange``. + +``css3_names_to_hex`` + Mapping of normalized CSS3 color names to normalized hexadecimal + values. + +``css3_hex_to_names`` + Mapping of normalized hexadecimal values to normalized CSS3 color + names. + +Normalization functions +----------------------- + +``normalize_hex(hex_value)`` + Normalize a hexadecimal color value to the following form and + return the result:: + + #[a-f0-9]{6} + + In other words, the following transformations are applied as + needed: + + * If the value contains only three hexadecimal digits, it is + expanded to six. + + * The value is normalized to lower-case. + + If the supplied value cannot be interpreted as a hexadecimal color + value, ``ValueError`` is raised. + + Example: + + >>> normalize_hex('#09c') + '#0099cc' + +Conversions from named colors +----------------------------- + +``name_to_hex(name, spec='css3')`` + Convert a color name to a normalized hexadecimal color value. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The color name will be normalized to lower-case before being + looked up, and when no color of that name exists in the given + specification, ``ValueError`` is raised. + + Example: + + >>> name_to_hex('deepskyblue') + '#00bfff' + +``name_to_rgb(name, spec='css3')`` + Convert a color name to a 3-tuple of integers suitable for use in + an ``rgb()`` triplet specifying that color. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The color name will be normalized to lower-case before being + looked up, and when no color of that name exists in the given + specification, ``ValueError`` is raised. + + Example: + + >>> name_to_rgb('navy') + (0, 0, 128) + +``name_to_rgb_percent(name, spec='css3')`` + Convert a color name to a 3-tuple of percentages suitable for use + in an ``rgb()`` triplet specifying that color. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The color name will be normalized to lower-case before being + looked up, and when no color of that name exists in the given + specification, ``ValueError`` is raised. + + Example: + + >>> name_to_rgb_percent('navy') + ('0%', '0%', '50%') + + +Conversions from hexadecimal values +----------------------------------- + +``hex_to_name(hex_value, spec='css3')`` + Convert a hexadecimal color value to its corresponding normalized + color name, if any such name exists. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The hexadecimal value will be normalized before being looked up, + and when no color name for the value is found in the given + specification, ``ValueError`` is raised. + + Example: + + >>> hex_to_name('#000080') + 'navy' + +``hex_to_rgb(hex_value)`` + Convert a hexadecimal color value to a 3-tuple of integers + suitable for use in an ``rgb()`` triplet specifying that color. + + The hexadecimal value will be normalized before being converted. + + Example: + + >>> hex_to_rgb('#000080') + (0, 0, 128) + +``hex_to_rgb_percent(hex_value)`` + Convert a hexadecimal color value to a 3-tuple of percentages + suitable for use in an ``rgb()`` triplet representing that color. + + The hexadecimal value will be normalized before converting. + + Example: + + >>> hex_to_rgb_percent('#ffffff') + ('100%', '100%', '100%') + + +Conversions from integer rgb() triplets +--------------------------------------- + +``rgb_to_name(rgb_triplet, spec='css3')`` + Convert a 3-tuple of integers, suitable for use in an ``rgb()`` + color triplet, to its corresponding normalized color name, if any + such name exists. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + If there is no matching name, ``ValueError`` is raised. + + Example: + + >>> rgb_to_name((0, 0, 0)) + 'black' + +``rgb_to_hex(rgb_triplet)`` + Convert a 3-tuple of integers, suitable for use in an ``rgb()`` + color triplet, to a normalized hexadecimal value for that color. + + Example: + + >>> rgb_to_hex((255, 255, 255)) + '#ffffff' + +``rgb_to_rgb_percent(rgb_triplet)`` + Convert a 3-tuple of integers, suitable for use in an ``rgb()`` + color triplet, to a 3-tuple of percentages suitable for use in + representing that color. + + This function makes some trade-offs in terms of the accuracy of + the final representation; for some common integer values, + special-case logic is used to ensure a precise result (e.g., + integer 128 will always convert to '50%', integer 32 will always + convert to '12.5%'), but for all other values a standard Python + ``float`` is used and rounded to two decimal places, which may + result in a loss of precision for some values. + + Examples: + + >>> rgb_to_rgb_percent((255, 255, 255)) + ('100%', '100%', '100%') + >>> rgb_to_rgb_percent((0, 0, 128)) + ('0%', '0%', '50%') + >>> rgb_to_rgb_percent((33, 56, 192)) + ('12.94%', '21.96%', '75.29%') + >>> rgb_to_rgb_percent((64, 32, 16)) + ('25%', '12.5%', '6.25%') + + +Conversions from percentage rgb() triplets +------------------------------------------ + +``rgb_percent_to_name(rgb_percent_triplet, spec='css3')`` + Convert a 3-tuple of percentages, suitable for use in an ``rgb()`` + color triplet, to its corresponding normalized color name, if any + such name exists. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + If there is no matching name, ``ValueError`` is raised. + + Example: + + >>> rgb_percent_to_name(('0%', '0%', '50%')) + 'navy' + +``rgb_percent_to_hex(rgb_percent_triplet)`` + Convert a 3-tuple of percentages, suitable for use in an ``rgb()`` + color triplet, to a normalized hexadecimal color value for that + color. + + Example: + + >>> rgb_percent_to_hex(('100%', '100%', '0%')) + '#ffff00' + +``rgb_percent_to_rgb(rgb_percent_triplet)`` + Convert a 3-tuple of percentages, suitable for use in an ``rgb()`` + color triplet, to a 3-tuple of integers suitable for use in + representing that color. + + Some precision may be lost in this conversion. See the note + regarding precision for ``rgb_to_rgb_percent()`` for details; + generally speaking, the following is true for any 3-tuple ``t`` of + integers in the range 0...255 inclusive:: + + t == rgb_percent_to_rgb(rgb_to_rgb_percent(t)) + + Examples: + + >>> rgb_percent_to_rgb(('100%', '100%', '100%')) + (255, 255, 255) + >>> rgb_percent_to_rgb(('0%', '0%', '50%')) + (0, 0, 128) + >>> rgb_percent_to_rgb(('25%', '12.5%', '6.25%')) + (64, 32, 16) + >>> rgb_percent_to_rgb(('12.94%', '21.96%', '75.29%')) + (33, 56, 192) diff --git a/tools/kothic/mapcss/webcolors/__init__.py b/tools/kothic/mapcss/webcolors/__init__.py new file mode 100644 index 0000000000..40a96afc6f --- /dev/null +++ b/tools/kothic/mapcss/webcolors/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/tools/kothic/mapcss/webcolors/webcolors.py b/tools/kothic/mapcss/webcolors/webcolors.py new file mode 100644 index 0000000000..6bcf7f95b3 --- /dev/null +++ b/tools/kothic/mapcss/webcolors/webcolors.py @@ -0,0 +1,880 @@ +# -*- coding: utf-8 -*- +""" +A simple library for working with the color names and color codes +defined by the HTML and CSS specifications. + +An overview of HTML and CSS colors +---------------------------------- + +Colors on the Web are specified in `the sRGB color space`_, where each +color is made up of a red component, a green component and a blue +component. This is useful because it maps (fairly) cleanly to the red, +green and blue components of pixels on a computer display, and to the +cone cells of a human eye, which come in three sets roughly +corresponding to the wavelengths of light associated with red, green +and blue. + +`The HTML 4 standard`_ defines two ways to specify sRGB colors: + +* A hash mark ('#') followed by three pairs of hexdecimal digits, + specifying values for red, green and blue components in that order; + for example, ``#0099cc``. Since each pair of hexadecimal digits can + express 256 different values, this allows up to 256**3 or 16,777,216 + unique colors to be specified (though, due to differences in display + technology, not all of these colors may be clearly distinguished on + any given physical display). + +* A set of predefined color names which correspond to specific + hexadecimal values; for example, ``white``. HTML 4 defines sixteen + such colors. + +`The CSS 2 standard`_ allows any valid HTML 4 color specification, and +adds three new ways to specify sRGB colors: + +* A hash mark followed by three hexadecimal digits, which is expanded + into three hexadecimal pairs by repeating each digit; thus ``#09c`` + is equivalent to ``#0099cc``. + +* The string 'rgb', followed by parentheses, between which are three + numeric values each between 0 and 255, inclusive, which are taken to + be the values of the red, green and blue components in that order; + for example, ``rgb(0, 153, 204)``. + +* The same as above, except using percentages instead of numeric + values; for example, ``rgb(0%, 60%, 80%)``. + +`The CSS 2.1 revision`_ does not add any new methods of specifying +sRGB colors, but does add one additional named color. + +`The CSS 3 color module`_ (currently a W3C Candidate Recommendation) +adds one new way to specify sRGB colors: + +* A hue-saturation-lightness triple (HSL), using the construct + ``hsl()``. + +It also adds support for variable opacity of colors, by allowing the +specification of alpha-channel information, through the ``rgba()`` and +``hsla()`` constructs, which are identical to ``rgb()`` and ``hsl()`` +with one exception: a fourth value is supplied, indicating the level +of opacity from ``0.0`` (completely transparent) to ``1.0`` +(completely opaque). Though not technically a color, the keyword +``transparent`` is also made available in lieu of a color value, and +corresponds to ``rgba(0,0,0,0)``. + +Additionally, CSS3 defines a new set of color names; this set is taken +directly from the named colors defined for SVG (Scalable Vector +Graphics) markup, and is a proper superset of the named colors defined +in CSS 2.1. This set also has significant overlap with traditional X11 +color sets as defined by the ``rgb.txt`` file on many Unix and +Unix-like operating systems, though the correspondence is not exact; +the set of X11 colors is not standardized, and the set of CSS3 colors +contains some definitions which diverge significantly from customary +X11 definitions (for example, CSS3's ``green`` is not equivalent to +X11's ``green``; the value which X11 designates ``green`` is +designated ``lime`` in CSS3). + +.. _the sRGB color space: http://www.w3.org/Graphics/Color/sRGB +.. _The HTML 4 standard: http://www.w3.org/TR/html401/types.html#h-6.5 +.. _The CSS 2 standard: http://www.w3.org/TR/REC-CSS2/syndata.html#value-def-color +.. _The CSS 2.1 revision: http://www.w3.org/TR/CSS21/ +.. _The CSS 3 color module: http://www.w3.org/TR/css3-color/ + +What this module supports +------------------------- + +The mappings and functions within this module support the following +methods of specifying sRGB colors, and conversions between them: + +* Six-digit hexadecimal. + +* Three-digit hexadecimal. + +* Integer ``rgb()`` triplet. + +* Percentage ``rgb()`` triplet. + +* Varying selections of predefined color names (see below). + +This module does not support ``hsl()`` triplets, nor does it support +opacity/alpha-channel information via ``rgba()`` or ``hsla()``. + +If you need to convert between RGB-specified colors and HSL-specified +colors, or colors specified via other means, consult `the colorsys +module`_ in the Python standard library, which can perform conversions +amongst several common color spaces. + +.. _the colorsys module: http://docs.python.org/library/colorsys.html + +Normalization +------------- + +For colors specified via hexadecimal values, this module will accept +input in the following formats: + +* A hash mark (#) followed by three hexadecimal digits, where letters + may be upper- or lower-case. + +* A hash mark (#) followed by six hexadecimal digits, where letters + may be upper- or lower-case. + +For output which consists of a color specified via hexadecimal values, +and for functions which perform intermediate conversion to hexadecimal +before returning a result in another format, this module always +normalizes such values to the following format: + +* A hash mark (#) followed by six hexadecimal digits, with letters + forced to lower-case. + +The function ``normalize_hex()`` in this module can be used to perform +this normalization manually if desired; see its documentation for an +explanation of the normalization process. + +For colors specified via predefined names, this module will accept +input in the following formats: + +* An entirely lower-case name, such as ``aliceblue``. + +* A name using initial capitals, such as ``AliceBlue``. + +For output which consists of a color specified via a predefined name, +and for functions which perform intermediate conversion to a +predefined name before returning a result in another format, this +module always normalizes such values to be entirely lower-case. + +Mappings of color names +----------------------- + +For each set of defined color names -- HTML 4, CSS 2, CSS 2.1 and CSS +3 -- this module exports two mappings: one of normalized color names +to normalized hexadecimal values, and one of normalized hexadecimal +values to normalized color names. These eight mappings are as follows: + +``html4_names_to_hex`` + Mapping of normalized HTML 4 color names to normalized hexadecimal + values. + +``html4_hex_to_names`` + Mapping of normalized hexadecimal values to normalized HTML 4 + color names. + +``css2_names_to_hex`` + Mapping of normalized CSS 2 color names to normalized hexadecimal + values. Because CSS 2 defines the same set of named colors as HTML + 4, this is merely an alias for ``html4_names_to_hex``. + +``css2_hex_to_names`` + Mapping of normalized hexadecimal values to normalized CSS 2 color + nams. For the reasons described above, this is merely an alias for + ``html4_hex_to_names``. + +``css21_names_to_hex`` + Mapping of normalized CSS 2.1 color names to normalized + hexadecimal values. This is identical to ``html4_names_to_hex``, + except for one addition: ``orange``. + +``css21_hex_to_names`` + Mapping of normalized hexadecimal values to normalized CSS 2.1 + color names. As above, this is identical to ``html4_hex_to_names`` + except for the addition of ``orange``. + +``css3_names_to_hex`` + Mapping of normalized CSS3 color names to normalized hexadecimal + values. + +``css3_hex_to_names`` + Mapping of normalized hexadecimal values to normalized CSS3 color + names. + +""" + +import math +import re +from hashlib import md5 + + +def _reversedict(d): + """ + Internal helper for generating reverse mappings; given a + dictionary, returns a new dictionary with keys and values swapped. + + """ + return dict(zip(d.values(), d.keys())) + +HEX_COLOR_RE = re.compile(r'^#([a-fA-F0-9]|[a-fA-F0-9]{3}|[a-fA-F0-9]{6})$') + +SUPPORTED_SPECIFICATIONS = ('html4', 'css2', 'css21', 'css3') + + +###################################################################### +# Mappings of color names to normalized hexadecimal color values. +###################################################################### + + +html4_names_to_hex = { + 'aqua': '#00ffff', + 'black': '#000000', + 'blue': '#0000ff', + 'fuchsia': '#ff00ff', + 'green': '#008000', + 'grey': '#808080', + 'lime': '#00ff00', + 'maroon': '#800000', + 'navy': '#000080', + 'olive': '#808000', + 'purple': '#800080', + 'red': '#ff0000', + 'silver': '#c0c0c0', + 'teal': '#008080', + 'white': '#ffffff', + 'yellow': '#ffff00' +} + +css2_names_to_hex = html4_names_to_hex + +css21_names_to_hex = dict(html4_names_to_hex, orange='#ffa500') + +css3_names_to_hex = { + 'aliceblue': '#f0f8ff', + 'antiquewhite': '#faebd7', + 'aqua': '#00ffff', + 'aquamarine': '#7fffd4', + 'azure': '#f0ffff', + 'beige': '#f5f5dc', + 'bisque': '#ffe4c4', + 'black': '#000000', + 'blanchedalmond': '#ffebcd', + 'blue': '#0000ff', + 'blueviolet': '#8a2be2', + 'brown': '#a52a2a', + 'burlywood': '#deb887', + 'cadetblue': '#5f9ea0', + 'chartreuse': '#7fff00', + 'chocolate': '#d2691e', + 'coral': '#ff7f50', + 'cornflowerblue': '#6495ed', + 'cornsilk': '#fff8dc', + 'crimson': '#dc143c', + 'cyan': '#00ffff', + 'darkblue': '#00008b', + 'darkcyan': '#008b8b', + 'darkgoldenrod': '#b8860b', + 'darkgray': '#a9a9a9', + 'darkgrey': '#a9a9a9', + 'darkgreen': '#006400', + 'darkkhaki': '#bdb76b', + 'darkmagenta': '#8b008b', + 'darkolivegreen': '#556b2f', + 'darkorange': '#ff8c00', + 'darkorchid': '#9932cc', + 'darkred': '#8b0000', + 'darksalmon': '#e9967a', + 'darkseagreen': '#8fbc8f', + 'darkslateblue': '#483d8b', + 'darkslategray': '#2f4f4f', + 'darkslategrey': '#2f4f4f', + 'darkturquoise': '#00ced1', + 'darkviolet': '#9400d3', + 'deeppink': '#ff1493', + 'deepskyblue': '#00bfff', + 'dimgray': '#696969', + 'dimgrey': '#696969', + 'dodgerblue': '#1e90ff', + 'firebrick': '#b22222', + 'floralwhite': '#fffaf0', + 'forestgreen': '#228b22', + 'fuchsia': '#ff00ff', + 'gainsboro': '#dcdcdc', + 'ghostwhite': '#f8f8ff', + 'gold': '#ffd700', + 'goldenrod': '#daa520', + 'gray': '#808080', + 'grey': '#808080', + 'green': '#008000', + 'greenyellow': '#adff2f', + 'honeydew': '#f0fff0', + 'hotpink': '#ff69b4', + 'indianred': '#cd5c5c', + 'indigo': '#4b0082', + 'ivory': '#fffff0', + 'khaki': '#f0e68c', + 'lavender': '#e6e6fa', + 'lavenderblush': '#fff0f5', + 'lawngreen': '#7cfc00', + 'lemonchiffon': '#fffacd', + 'lightblue': '#add8e6', + 'lightcoral': '#f08080', + 'lightcyan': '#e0ffff', + 'lightgoldenrodyellow': '#fafad2', + 'lightgray': '#d3d3d3', + 'lightgrey': '#d3d3d3', + 'lightgreen': '#90ee90', + 'lightpink': '#ffb6c1', + 'lightsalmon': '#ffa07a', + 'lightseagreen': '#20b2aa', + 'lightskyblue': '#87cefa', + 'lightslategray': '#778899', + 'lightslategrey': '#778899', + 'lightsteelblue': '#b0c4de', + 'lightyellow': '#ffffe0', + 'lime': '#00ff00', + 'limegreen': '#32cd32', + 'linen': '#faf0e6', + 'magenta': '#ff00ff', + 'maroon': '#800000', + 'mediumaquamarine': '#66cdaa', + 'mediumblue': '#0000cd', + 'mediumorchid': '#ba55d3', + 'mediumpurple': '#9370d8', + 'mediumseagreen': '#3cb371', + 'mediumslateblue': '#7b68ee', + 'mediumspringgreen': '#00fa9a', + 'mediumturquoise': '#48d1cc', + 'mediumvioletred': '#c71585', + 'midnightblue': '#191970', + 'mintcream': '#f5fffa', + 'mistyrose': '#ffe4e1', + 'moccasin': '#ffe4b5', + 'navajowhite': '#ffdead', + 'navy': '#000080', + 'oldlace': '#fdf5e6', + 'olive': '#808000', + 'olivedrab': '#6b8e23', + 'orange': '#ffa500', + 'orangered': '#ff4500', + 'orchid': '#da70d6', + 'palegoldenrod': '#eee8aa', + 'palegreen': '#98fb98', + 'paleturquoise': '#afeeee', + 'palevioletred': '#d87093', + 'papayawhip': '#ffefd5', + 'peachpuff': '#ffdab9', + 'peru': '#cd853f', + 'pink': '#ffc0cb', + 'plum': '#dda0dd', + 'powderblue': '#b0e0e6', + 'purple': '#800080', + 'red': '#ff0000', + 'rosybrown': '#bc8f8f', + 'royalblue': '#4169e1', + 'saddlebrown': '#8b4513', + 'salmon': '#fa8072', + 'sandybrown': '#f4a460', + 'seagreen': '#2e8b57', + 'seashell': '#fff5ee', + 'sienna': '#a0522d', + 'silver': '#c0c0c0', + 'skyblue': '#87ceeb', + 'slateblue': '#6a5acd', + 'slategray': '#708090', + 'slategrey': '#708090', + 'snow': '#fffafa', + 'springgreen': '#00ff7f', + 'steelblue': '#4682b4', + 'tan': '#d2b48c', + 'teal': '#008080', + 'thistle': '#d8bfd8', + 'tomato': '#ff6347', + 'turquoise': '#40e0d0', + 'violet': '#ee82ee', + 'wheat': '#f5deb3', + 'white': '#ffffff', + 'whitesmoke': '#f5f5f5', + 'yellow': '#ffff00', + 'yellowgreen': '#9acd32', +} + + +###################################################################### +# Mappings of normalized hexadecimal color values to color names. +###################################################################### + + +html4_hex_to_names = _reversedict(html4_names_to_hex) + +css2_hex_to_names = html4_hex_to_names + +css21_hex_to_names = _reversedict(css21_names_to_hex) + +css3_hex_to_names = _reversedict(css3_names_to_hex) + + +###################################################################### +# Normalization routines. +###################################################################### + + +def normalize_hex(hex_value): + """ + Normalize a hexadecimal color value to the following form and + return the result:: + + #[a-f0-9]{6} + + In other words, the following transformations are applied as + needed: + + * If the value contains only three hexadecimal digits, it is + expanded to six. + + * The value is normalized to lower-case. + + If the supplied value cannot be interpreted as a hexadecimal color + value, ``ValueError`` is raised. + + Examples: + + >>> normalize_hex('#09c') + '#0099cc' + >>> normalize_hex('#0099cc') + '#0099cc' + >>> normalize_hex('#09C') + '#0099cc' + >>> normalize_hex('#0099CC') + '#0099cc' + >>> normalize_hex('0099cc') + Traceback (most recent call last): + ... + ValueError: '0099cc' is not a valid hexadecimal color value. + >>> normalize_hex('#0099QX') + Traceback (most recent call last): + ... + ValueError: '#0099QX' is not a valid hexadecimal color value. + >>> normalize_hex('foobarbaz') + Traceback (most recent call last): + ... + ValueError: 'foobarbaz' is not a valid hexadecimal color value. + >>> normalize_hex('#0') + Traceback (most recent call last): + ... + ValueError: '#0' is not a valid hexadecimal color value. + + """ + try: + hex_digits = HEX_COLOR_RE.match(hex_value).groups()[0] + except AttributeError: + raise ValueError("'%s' is not a valid hexadecimal color value." % hex_value) + if len(hex_digits) == 3: + hex_digits = ''.join(map(lambda s: 2 * s, hex_digits)) + elif len(hex_digits) == 1: + hex_digits = hex_digits * 6 + return '#%s' % hex_digits.lower() + + +###################################################################### +# Conversions from color names to various formats. +###################################################################### + + +def name_to_hex(name, spec='css3'): + """ + Convert a color name to a normalized hexadecimal color value. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The color name will be normalized to lower-case before being + looked up, and when no color of that name exists in the given + specification, ``ValueError`` is raised. + + Examples: + + >>> name_to_hex('deepskyblue') + '#00bfff' + >>> name_to_hex('DeepSkyBlue') + '#00bfff' + >>> name_to_hex('white', spec='html4') + '#ffffff' + >>> name_to_hex('white', spec='css2') + '#ffffff' + >>> name_to_hex('white', spec='css21') + '#ffffff' + >>> name_to_hex('white', spec='css3') + '#ffffff' + >>> name_to_hex('white', spec='css4') + Traceback (most recent call last): + ... + TypeError: 'css4' is not a supported specification for color name lookups; supported specifications are: html4, css2, css21, css3. + >>> name_to_hex('deepskyblue', spec='css2') + Traceback (most recent call last): + ... + ValueError: 'deepskyblue' is not defined as a named color in css2. + + """ + if spec not in SUPPORTED_SPECIFICATIONS: + raise TypeError("'%s' is not a supported specification for color name lookups; supported specifications are: %s." % (spec, + ', '.join(SUPPORTED_SPECIFICATIONS))) + normalized = name.lower() + try: + hex_value = globals()['%s_names_to_hex' % spec][normalized] + except KeyError: + raise ValueError("'%s' is not defined as a named color in %s." % (name, spec)) + return hex_value + + +def name_to_rgb(name, spec='css3'): + """ + Convert a color name to a 3-tuple of integers suitable for use in + an ``rgb()`` triplet specifying that color. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The color name will be normalized to lower-case before being + looked up, and when no color of that name exists in the given + specification, ``ValueError`` is raised. + + Examples: + + >>> name_to_rgb('navy') + (0, 0, 128) + >>> name_to_rgb('cadetblue') + (95, 158, 160) + >>> name_to_rgb('cadetblue', spec='html4') + Traceback (most recent call last): + ... + ValueError: 'cadetblue' is not defined as a named color in html4. + + """ + return hex_to_rgb(name_to_hex(name, spec=spec)) + + +def name_to_rgb_percent(name, spec='css3'): + """ + Convert a color name to a 3-tuple of percentages suitable for use + in an ``rgb()`` triplet specifying that color. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The color name will be normalized to lower-case before being + looked up, and when no color of that name exists in the given + specification, ``ValueError`` is raised. + + Examples: + + >>> name_to_rgb_percent('white') + ('100%', '100%', '100%') + >>> name_to_rgb_percent('navy') + ('0%', '0%', '50%') + >>> name_to_rgb_percent('goldenrod') + ('85.49%', '64.71%', '12.5%') + + """ + return rgb_to_rgb_percent(name_to_rgb(name, spec=spec)) + + +###################################################################### +# Conversions from hexadecimal color values to various formats. +###################################################################### + + +def hex_to_name(hex_value, spec='css3'): + """ + Convert a hexadecimal color value to its corresponding normalized + color name, if any such name exists. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + The hexadecimal value will be normalized before being looked up, + and when no color name for the value is found in the given + specification, ``ValueError`` is raised. + + Examples: + + >>> hex_to_name('#000080') + 'navy' + >>> hex_to_name('#000080', spec='html4') + 'navy' + >>> hex_to_name('#000080', spec='css2') + 'navy' + >>> hex_to_name('#000080', spec='css21') + 'navy' + >>> hex_to_name('#8b4513') + 'saddlebrown' + >>> hex_to_name('#8b4513', spec='html4') + Traceback (most recent call last): + ... + ValueError: '#8b4513' has no defined color name in html4. + >>> hex_to_name('#8b4513', spec='css4') + Traceback (most recent call last): + ... + TypeError: 'css4' is not a supported specification for color name lookups; supported specifications are: html4, css2, css21, css3. + + """ + if spec not in SUPPORTED_SPECIFICATIONS: + raise TypeError("'%s' is not a supported specification for color name lookups; supported specifications are: %s." % (spec, + ', '.join(SUPPORTED_SPECIFICATIONS))) + normalized = normalize_hex(hex_value) + try: + name = globals()['%s_hex_to_names' % spec][normalized] + except KeyError: + raise ValueError("'%s' has no defined color name in %s." % (hex_value, spec)) + return name + + +def any_hex_to_name(hex_value): + try: + return hex_to_name(hex_value) + except ValueError: + return hex_value + + +def hex_to_rgb(hex_value): + """ + Convert a hexadecimal color value to a 3-tuple of integers + suitable for use in an ``rgb()`` triplet specifying that color. + + The hexadecimal value will be normalized before being converted. + + Examples: + + >>> hex_to_rgb('#000080') + (0, 0, 128) + >>> hex_to_rgb('#ffff00') + (255, 255, 0) + >>> hex_to_rgb('#f00') + (255, 0, 0) + >>> hex_to_rgb('#deb887') + (222, 184, 135) + + """ + hex_digits = normalize_hex(hex_value) + return tuple(map(lambda s: int(s, 16), + (hex_digits[1:3], hex_digits[3:5], hex_digits[5:7]))) + + +def hex_to_rgb_percent(hex_value): + """ + Convert a hexadecimal color value to a 3-tuple of percentages + suitable for use in an ``rgb()`` triplet representing that color. + + The hexadecimal value will be normalized before converting. + + Examples: + + >>> hex_to_rgb_percent('#ffffff') + ('100%', '100%', '100%') + >>> hex_to_rgb_percent('#000080') + ('0%', '0%', '50%') + + """ + return rgb_to_rgb_percent(hex_to_rgb(hex_value)) + + +###################################################################### +# Conversions from integer rgb() triplets to various formats. +###################################################################### + + +def rgb_to_name(rgb_triplet, spec='css3'): + """ + Convert a 3-tuple of integers, suitable for use in an ``rgb()`` + color triplet, to its corresponding normalized color name, if any + such name exists. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + If there is no matching name, ``ValueError`` is raised. + + Examples: + + >>> rgb_to_name((0, 0, 0)) + 'black' + >>> rgb_to_name((0, 0, 128)) + 'navy' + >>> rgb_to_name((95, 158, 160)) + 'cadetblue' + + """ + return hex_to_name(rgb_to_hex(rgb_triplet), spec=spec) + + +def rgb_to_hex(rgb_triplet): + """ + Convert a 3-tuple of integers, suitable for use in an ``rgb()`` + color triplet, to a normalized hexadecimal value for that color. + + Examples: + + >>> rgb_to_hex((255, 255, 255)) + '#ffffff' + >>> rgb_to_hex((0, 0, 128)) + '#000080' + >>> rgb_to_hex((33, 56, 192)) + '#2138c0' + + """ + return '#%02x%02x%02x' % rgb_triplet + + +def rgb_to_rgb_percent(rgb_triplet): + """ + Convert a 3-tuple of integers, suitable for use in an ``rgb()`` + color triplet, to a 3-tuple of percentages suitable for use in + representing that color. + + This function makes some trade-offs in terms of the accuracy of + the final representation; for some common integer values, + special-case logic is used to ensure a precise result (e.g., + integer 128 will always convert to '50%', integer 32 will always + convert to '12.5%'), but for all other values a standard Python + ``float`` is used and rounded to two decimal places, which may + result in a loss of precision for some values. + + Examples: + + >>> rgb_to_rgb_percent((255, 255, 255)) + ('100%', '100%', '100%') + >>> rgb_to_rgb_percent((0, 0, 128)) + ('0%', '0%', '50%') + >>> rgb_to_rgb_percent((33, 56, 192)) + ('12.94%', '21.96%', '75.29%') + >>> rgb_to_rgb_percent((64, 32, 16)) + ('25%', '12.5%', '6.25%') + + """ + # In order to maintain precision for common values, + # 256 / 2**n is special-cased for values of n + # from 0 through 4, as well as 0 itself. + specials = {255: '100%', 128: '50%', 64: '25%', + 32: '12.5%', 16: '6.25%', 0: '0%'} + return tuple(map(lambda d: specials.get(d, '%.02f%%' % ((d / 255.0) * 100)), + rgb_triplet)) + + +###################################################################### +# Conversions from percentage rgb() triplets to various formats. +###################################################################### + + +def rgb_percent_to_name(rgb_percent_triplet, spec='css3'): + """ + Convert a 3-tuple of percentages, suitable for use in an ``rgb()`` + color triplet, to its corresponding normalized color name, if any + such name exists. + + The optional keyword argument ``spec`` determines which + specification's list of color names will be used; valid values are + ``html4``, ``css2``, ``css21`` and ``css3``, and the default is + ``css3``. + + If there is no matching name, ``ValueError`` is raised. + + Examples: + + >>> rgb_percent_to_name(('0%', '0%', '0%')) + 'black' + >>> rgb_percent_to_name(('0%', '0%', '50%')) + 'navy' + >>> rgb_percent_to_name(('85.49%', '64.71%', '12.5%')) + 'goldenrod' + + """ + return rgb_to_name(rgb_percent_to_rgb(rgb_percent_triplet), spec=spec) + + +def rgb_percent_to_hex(rgb_percent_triplet): + """ + Convert a 3-tuple of percentages, suitable for use in an ``rgb()`` + color triplet, to a normalized hexadecimal color value for that + color. + + Examples: + + >>> rgb_percent_to_hex(('100%', '100%', '0%')) + '#ffff00' + >>> rgb_percent_to_hex(('0%', '0%', '50%')) + '#000080' + >>> rgb_percent_to_hex(('85.49%', '64.71%', '12.5%')) + '#daa520' + + """ + return rgb_to_hex(rgb_percent_to_rgb(rgb_percent_triplet)) + + +def _percent_to_integer(percent): + """ + Internal helper for converting a percentage value to an integer + between 0 and 255 inclusive. + + """ + num = float(percent.split('%')[0]) / 100.0 * 255 + e = num - math.floor(num) + return e < 0.5 and int(math.floor(num)) or int(math.ceil(num)) + + +def rgb_percent_to_rgb(rgb_percent_triplet): + """ + Convert a 3-tuple of percentages, suitable for use in an ``rgb()`` + color triplet, to a 3-tuple of integers suitable for use in + representing that color. + + Some precision may be lost in this conversion. See the note + regarding precision for ``rgb_to_rgb_percent()`` for details; + generally speaking, the following is true for any 3-tuple ``t`` of + integers in the range 0...255 inclusive:: + + t == rgb_percent_to_rgb(rgb_to_rgb_percent(t)) + + Examples: + + >>> rgb_percent_to_rgb(('100%', '100%', '100%')) + (255, 255, 255) + >>> rgb_percent_to_rgb(('0%', '0%', '50%')) + (0, 0, 128) + >>> rgb_percent_to_rgb(('25%', '12.5%', '6.25%')) + (64, 32, 16) + >>> rgb_percent_to_rgb(('12.94%', '21.96%', '75.29%')) + (33, 56, 192) + + """ + return tuple(map(_percent_to_integer, rgb_percent_triplet)) + + +def whatever_to_rgb(string): + """ + Converts CSS3 color or a hex into rgb triplet; hash of string if fails. + """ + string = string.strip().lower() + try: + return name_to_rgb(string) + except ValueError: + try: + return hex_to_rgb(string) + except ValueError: + try: + if string[:3] == "rgb": + return tuple([float(i) for i in string[4:-1].split(",")][0:3]) + except: + return hex_to_rgb("#" + md5(string).hexdigest()[:6]) + + +def whatever_to_hex(string): + if type(string) == tuple: + return cairo_to_hex(string).upper() + return rgb_to_hex(whatever_to_rgb(string)).upper() + + +def whatever_to_cairo(string): + a = whatever_to_rgb(string) + return a[0] / 255., a[1] / 255., a[2] / 255. + + +def cairo_to_hex(cairo): + return rgb_to_hex((cairo[0] * 255., cairo[1] * 255., cairo[2] * 255.)) + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/tools/kothic/timer.py b/tools/kothic/timer.py new file mode 100644 index 0000000000..5da95b7c5f --- /dev/null +++ b/tools/kothic/timer.py @@ -0,0 +1,42 @@ +from timeit import default_timer + +class Timer(object): + def __init__(self): + self.timer = default_timer + self.start = self.timer() + + def Reset(self): + self.start = self.timer() + + def ElapsedMsec(self): + elapsed_secs = self.timer() - self.start + return elapsed_secs * 1000 + + def ElapsedSec(self): + elapsed_secs = self.timer() - self.start + return elapsed_secs + +class AccumulativeTimer(object): + def __init__(self): + self.timer = default_timer + self.elapsed_secs = 0 + self.start = 0 + self.count = 0 + + def Start(self): + self.start = self.timer() + + def Stop(self): + self.elapsed_secs += self.timer() - self.start + self.start = 0 + self.count += 1 + + def ElapsedMsec(self): + elapsed_msec = self.elapsed_secs * 1000 + return elapsed_msec + + def ElapsedSec(self): + return self.elapsed_secs + + def Count(self): + return self.count diff --git a/tools/unix/generate_drules.sh b/tools/unix/generate_drules.sh new file mode 100755 index 0000000000..78c7c21e15 --- /dev/null +++ b/tools/unix/generate_drules.sh @@ -0,0 +1,5 @@ +echo "Build drawing rules light" +python ../kothic/libkomwm.py -s ../../data/styles/normal_light.mapcss -o ../../data/drules_proto + +echo "Build drawing rules dark" +python ../kothic/libkomwm.py -s ../../data/styles/normal_dark.mapcss -o ../../data/drules_proto_dark