mcc2 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/.autotest +23 -0
- data/.gitignore +15 -0
- data/.rubocop.yml +3 -0
- data/Gemfile +13 -0
- data/History.txt +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +51 -0
- data/Rakefile +12 -0
- data/config/i18n-tasks.yml +193 -0
- data/exe/mcc2 +18 -0
- data/exe/mcc2-generate-preview +17 -0
- data/exe/mcc2print +6 -0
- data/lib/libguib16.rb +1620 -0
- data/lib/mcc2/advanced_data_edit_dlg.rb +139 -0
- data/lib/mcc2/cc_dialogbox.rb +52 -0
- data/lib/mcc2/constants.rb +154 -0
- data/lib/mcc2/dataman/base.rb +61 -0
- data/lib/mcc2/dataman/csv.rb +49 -0
- data/lib/mcc2/dataman/database.rb +8 -0
- data/lib/mcc2/dataman/dbf.rb +45 -0
- data/lib/mcc2/dataman/none.rb +25 -0
- data/lib/mcc2/dataview.rb +348 -0
- data/lib/mcc2/dataview_dlg.rb +232 -0
- data/lib/mcc2/debug_tools.rb +20 -0
- data/lib/mcc2/fx_colored_item.rb +101 -0
- data/lib/mcc2/generate_preview.rb +68 -0
- data/lib/mcc2/gui/advanced_data_edit_dlg.rbin +0 -0
- data/lib/mcc2/gui/dataview_dlg.pp +522 -0
- data/lib/mcc2/gui/layoutview_dlg.pp +5115 -0
- data/lib/mcc2/gui/mcc2_mainwin.pp +838 -0
- data/lib/mcc2/gui/picture_data_edit_dlg.rbin +0 -0
- data/lib/mcc2/gui/printerview_dlg.pp +3311 -0
- data/lib/mcc2/images/Image-Arrow-Left.gif +0 -0
- data/lib/mcc2/images/Image-Arrow-Right.gif +0 -0
- data/lib/mcc2/images/Image-FastArrowLeft.gif +0 -0
- data/lib/mcc2/images/Image-FastArrowRight.bmp +0 -0
- data/lib/mcc2/images/Image-FastArrowRight.gif +0 -0
- data/lib/mcc2/images/application-exit.png +0 -0
- data/lib/mcc2/images/copy.png +0 -0
- data/lib/mcc2/images/country_de.png +0 -0
- data/lib/mcc2/images/country_gb.png +0 -0
- data/lib/mcc2/images/country_rs.png +0 -0
- data/lib/mcc2/images/cut.png +0 -0
- data/lib/mcc2/images/distribute-horizontal-baseline.png +0 -0
- data/lib/mcc2/images/distribute-vertical-baseline.png +0 -0
- data/lib/mcc2/images/document-close.png +0 -0
- data/lib/mcc2/images/document-edit.png +0 -0
- data/lib/mcc2/images/document-new.png +0 -0
- data/lib/mcc2/images/document-open.png +0 -0
- data/lib/mcc2/images/document-print-frame.png +0 -0
- data/lib/mcc2/images/document-print.png +0 -0
- data/lib/mcc2/images/document-save-as.png +0 -0
- data/lib/mcc2/images/document-save.png +0 -0
- data/lib/mcc2/images/documentation.png +0 -0
- data/lib/mcc2/images/draw-rectangle.png +0 -0
- data/lib/mcc2/images/dummy.gif +0 -0
- data/lib/mcc2/images/exchange-positions.png +0 -0
- data/lib/mcc2/images/insert-image.png +0 -0
- data/lib/mcc2/images/insert-text.png +0 -0
- data/lib/mcc2/images/libreoffice-oasis-master-document.png +0 -0
- data/lib/mcc2/images/measure.png +0 -0
- data/lib/mcc2/images/messagebox_warning.png +0 -0
- data/lib/mcc2/images/paste.png +0 -0
- data/lib/mcc2/images/process-stop.png +0 -0
- data/lib/mcc2/images/zoom-in.png +0 -0
- data/lib/mcc2/layout.rb +323 -0
- data/lib/mcc2/layout_file.rb +794 -0
- data/lib/mcc2/layout_object/barcode.rb +33 -0
- data/lib/mcc2/layout_object/box.rb +18 -0
- data/lib/mcc2/layout_object/chip.rb +54 -0
- data/lib/mcc2/layout_object/embed.rb +16 -0
- data/lib/mcc2/layout_object/go_attribut.rb +16 -0
- data/lib/mcc2/layout_object/graphic.rb +59 -0
- data/lib/mcc2/layout_object/magstripe.rb +13 -0
- data/lib/mcc2/layout_object/picture.rb +22 -0
- data/lib/mcc2/layout_object/text.rb +29 -0
- data/lib/mcc2/layout_win.rb +2052 -0
- data/lib/mcc2/layoutview.rb +845 -0
- data/lib/mcc2/layoutview_dlg.rb +2058 -0
- data/lib/mcc2/locales.rb +27 -0
- data/lib/mcc2/mcc2_mainwin.rb +453 -0
- data/lib/mcc2/mcc2gui.rb +295 -0
- data/lib/mcc2/mcc2print.rb +54 -0
- data/lib/mcc2/picture_data_edit_dlg.rb +139 -0
- data/lib/mcc2/print_dlg.rb +520 -0
- data/lib/mcc2/printer/base.rb +148 -0
- data/lib/mcc2/printer/card_printer.rb +45 -0
- data/lib/mcc2/printer/file_printer.rb +135 -0
- data/lib/mcc2/printer/gdi_card_printer.rb +14 -0
- data/lib/mcc2/printer/gdi_printer.rb +197 -0
- data/lib/mcc2/printer/linux_cups_printer.rb +92 -0
- data/lib/mcc2/printer/preview.rb +277 -0
- data/lib/mcc2/printer/printer_cab.rb +118 -0
- data/lib/mcc2/printer/printer_cx320_direct.rb +286 -0
- data/lib/mcc2/printer/printer_epl.rb +118 -0
- data/lib/mcc2/printer/printer_ttp34x.rb +124 -0
- data/lib/mcc2/printer/printer_zebra.rb +324 -0
- data/lib/mcc2/printer/svg_drawer.rb +689 -0
- data/lib/mcc2/printerview.rb +604 -0
- data/lib/mcc2/printerview_dlg.rb +1248 -0
- data/lib/mcc2/version.rb +3 -0
- data/lib/mcc2.rb +11 -0
- data/locales/de.yml +6 -0
- data/locales/en.yml +502 -0
- data/locales/rs.yml +502 -0
- data/mcc2.gemspec +41 -0
- data/test/dummy.rb +19 -0
- data/test/layouts/bart_simpson.png +0 -0
- data/test/layouts/color_test.xml +151 -0
- data/test/layouts/ms.xml +67 -0
- data/test/ruby_print.rb +380 -0
- data/test/test.csv +4 -0
- data/test/test.dbf +0 -0
- data/test/test.str +4 -0
- data/test/test_mcc2.rb +8 -0
- data.tar.gz.sig +6 -0
- metadata +421 -0
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,689 @@
|
|
|
1
|
+
require "mcc2/dataman/base"
|
|
2
|
+
require "i18n"
|
|
3
|
+
require "ttfunk"
|
|
4
|
+
require "pango"
|
|
5
|
+
|
|
6
|
+
module Mcc2
|
|
7
|
+
module SvgDrawer
|
|
8
|
+
class ObjRectangle
|
|
9
|
+
def initialize(v1, v2, v3, v4, obj_id)
|
|
10
|
+
@v1 = v1
|
|
11
|
+
@v2 = v2
|
|
12
|
+
@v3 = v3
|
|
13
|
+
@v4 = v4
|
|
14
|
+
@obj_id = obj_id
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private def sign(p1, p2, p3)
|
|
18
|
+
(p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def point_inside(pt)
|
|
22
|
+
d1 = sign(pt, @v1, @v2)
|
|
23
|
+
d2 = sign(pt, @v2, @v3)
|
|
24
|
+
d3 = sign(pt, @v3, @v4)
|
|
25
|
+
d4 = sign(pt, @v4, @v1)
|
|
26
|
+
|
|
27
|
+
has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0) || (d4 < 0)
|
|
28
|
+
has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0) || (d4 > 0)
|
|
29
|
+
|
|
30
|
+
!(has_neg && has_pos)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
attr_reader :obj_id
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class ObjRectangeList
|
|
37
|
+
def initialize
|
|
38
|
+
@objs = []
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def add(rect)
|
|
42
|
+
if rect.is_a?(self.class)
|
|
43
|
+
@objs += rect.objs
|
|
44
|
+
else
|
|
45
|
+
@objs << rect
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def point_inside(pt)
|
|
50
|
+
@objs.select do |obj|
|
|
51
|
+
obj.point_inside(pt)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
attr_reader :objs
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def width_with_margins(width)
|
|
59
|
+
width + card_margin_left + card_margin_right
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def height_with_margins(height)
|
|
63
|
+
height + card_margin_bottom + card_margin_top
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
FontInfo = Data.define :font_family, :font_weight, :font_style, :font_stretch
|
|
67
|
+
|
|
68
|
+
private def fontinfo_from_file(font_path)
|
|
69
|
+
ttf = TTFunk::File.open(font_path)
|
|
70
|
+
|
|
71
|
+
# Extract names from the 'name' table
|
|
72
|
+
# ttfunk groups strings by name_id. We fetch the English (US) strings primarily.
|
|
73
|
+
names = ttf.name
|
|
74
|
+
|
|
75
|
+
# Helper to get the English string for a specific Name ID
|
|
76
|
+
get_name = lambda do |instances|
|
|
77
|
+
return nil if instances.empty?
|
|
78
|
+
|
|
79
|
+
# Try to find English (Language ID 1033 or 0)
|
|
80
|
+
raw_string = instances.find { |s| s.language_id == 1033 || s.language_id == 0 } || instances.first
|
|
81
|
+
# Determine Ruby-compatible encoding based on TTF Platform Specs
|
|
82
|
+
ruby_encoding = case raw_string.platform_id
|
|
83
|
+
when 0, 3 # Unicode (0) or Microsoft (3) use UTF-16BE/UCS-2
|
|
84
|
+
'UTF-16BE'
|
|
85
|
+
when 1 # Macintosh (1) typically uses MacRoman
|
|
86
|
+
'MacRoman'
|
|
87
|
+
else
|
|
88
|
+
'ASCII-8BIT' # Fallback for raw byte manipulation
|
|
89
|
+
end
|
|
90
|
+
utf8_string = raw_string.encode('UTF-8', ruby_encoding, invalid: :replace, undef: :replace)
|
|
91
|
+
# Strip null bytes that occasionally bleed through from malformed sub-tables
|
|
92
|
+
utf8_string.gsub("\u0000", '').strip
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Fetch the specific Name IDs
|
|
96
|
+
font_family_id1 = get_name.call(names.font_family) # Font Family Name (Legacy)
|
|
97
|
+
font_subfamily_id2 = get_name.call(names.font_subfamily) # Font Subfamily Name (Legacy)
|
|
98
|
+
pref_family_id16 = get_name.call(names.preferred_family) # Typographic/Preferred Family
|
|
99
|
+
pref_subfamily_id17 = get_name.call(names.preferred_subfamily) # Typographic/Preferred Subfamily
|
|
100
|
+
|
|
101
|
+
# 1. Map font-family (Prefer ID 16, fallback to ID 1)
|
|
102
|
+
css_family = pref_family_id16 || font_family_id1
|
|
103
|
+
|
|
104
|
+
# 2. Map font-weight (Read directly from the numeric OS/2 table usWeightClass)
|
|
105
|
+
# Fallback to checking the Subfamily strings if OS/2 table is missing
|
|
106
|
+
css_weight = 400
|
|
107
|
+
if ttf.os2
|
|
108
|
+
css_weight = ttf.os2.weight_class
|
|
109
|
+
css_width = {
|
|
110
|
+
1 => 'ultra-condensed',
|
|
111
|
+
2 => 'extra-condensed',
|
|
112
|
+
3 => 'condensed',
|
|
113
|
+
4 => 'semi-condensed',
|
|
114
|
+
5 => 'normal',
|
|
115
|
+
6 => 'semi-expanded',
|
|
116
|
+
7 => 'expanded',
|
|
117
|
+
8 => 'extra-expanded',
|
|
118
|
+
9 => 'ultra-expanded',
|
|
119
|
+
}[ttf.os2.width_class]
|
|
120
|
+
else
|
|
121
|
+
subfamily_str = (pref_subfamily_id17 || font_subfamily_id2 || '').downcase
|
|
122
|
+
css_weight = 700 if subfamily_str.include?('bold')
|
|
123
|
+
css_weight = 300 if subfamily_str.include?('light')
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# 3. Map font-style (Check head table macStyle or parse Subfamily strings)
|
|
127
|
+
if ttf.header.mac_style
|
|
128
|
+
is_italic = (ttf.header.mac_style & 2) != 0 # Bit 1 represents Italic
|
|
129
|
+
else
|
|
130
|
+
subfamily_str = (pref_subfamily_id17 || font_subfamily_id2 || '').downcase
|
|
131
|
+
is_italic = subfamily_str.include?('italic') || subfamily_str.include?('oblique')
|
|
132
|
+
end
|
|
133
|
+
css_style = is_italic ? 'italic' : 'normal'
|
|
134
|
+
|
|
135
|
+
FontInfo.new(css_family, css_weight, css_style, css_width || '')
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
@@builtin_fonts = nil
|
|
139
|
+
def self.builtin_font_names
|
|
140
|
+
@@builtin_fonts ||= begin
|
|
141
|
+
fm = PangoCairo::FontMap.default
|
|
142
|
+
fm.families.flat_map do |f|
|
|
143
|
+
f.faces.map do |ff|
|
|
144
|
+
[ff.describe.to_s, FontInfo.new(f.name, ff.describe.weight.to_i, ff.describe.style.nick, ff.describe.stretch.nick)]
|
|
145
|
+
end
|
|
146
|
+
end.to_h.freeze
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
@@font_map = SvgDrawer.builtin_font_names.dup
|
|
151
|
+
@@pango_font_map = Pango::CairoFontMap.default
|
|
152
|
+
|
|
153
|
+
def add_fonts_from_layout(layout_file)
|
|
154
|
+
layout_file.sorted_id_list.each do |id|
|
|
155
|
+
go = layout_file.find_graphic_object_by_id(id)
|
|
156
|
+
font = go[GOA_FONT_TYP]
|
|
157
|
+
if font == "from_file"
|
|
158
|
+
font_path = go.layout_file.expand_path(go[GOA_FONT_FILE])
|
|
159
|
+
|
|
160
|
+
@@font_map[font_path] ||= begin
|
|
161
|
+
@@pango_font_map.add_font_file(font_path)
|
|
162
|
+
fontinfo_from_file(font_path)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def font_map
|
|
169
|
+
@@font_map
|
|
170
|
+
end
|
|
171
|
+
def pango_font_map
|
|
172
|
+
@@pango_font_map
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def flip_flop_mirrors(svg, tra, layout_file, preview, page)
|
|
176
|
+
flip = true if page % 2 == 0 && layout_file.get_attr_value(S_FRONT_FLIP) == S_TRUE
|
|
177
|
+
flop = true if page % 2 == 0 && layout_file.get_attr_value(S_FRONT_FLOP) == S_TRUE
|
|
178
|
+
flip = true if page % 2 != 0 && layout_file.get_attr_value(S_BACK_FLIP) == S_TRUE
|
|
179
|
+
flop = true if page % 2 != 0 && layout_file.get_attr_value(S_BACK_FLOP) == S_TRUE
|
|
180
|
+
|
|
181
|
+
return yield if (!flip && !flop) || preview
|
|
182
|
+
|
|
183
|
+
width = layout_file.get_attr_value(S_WIDTH).to_f
|
|
184
|
+
height = layout_file.get_attr_value(S_HEIGHT).to_f
|
|
185
|
+
image_width = mm(width_with_margins(width)).round-1
|
|
186
|
+
image_height = mm(height_with_margins(height)).round-1
|
|
187
|
+
|
|
188
|
+
svg.g(transform: "translate(#{tra.x}, #{tra.y}) scale(#{flip ? -1.0 : 1.0} #{flop ? -1.0 : 1.0}) translate(#{flip ? -image_width : 0} #{flop ? -image_height : 0}) translate(#{-tra.x}, #{-tra.y})") do
|
|
189
|
+
return yield
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def draw(drawing, transform, layout_file, preview, selected_object_ids, row, page_nr, layers: [0])
|
|
194
|
+
add_fonts_from_layout(layout_file)
|
|
195
|
+
|
|
196
|
+
flip_flop_mirrors(drawing, transform, layout_file, preview, page_nr) do
|
|
197
|
+
barcode_dpi = layout_file.get_attr_value(S_BARCODE_DPI).to_f
|
|
198
|
+
rect_list = ObjRectangeList.new
|
|
199
|
+
|
|
200
|
+
width = layout_file.get_attr_value(S_WIDTH).to_f
|
|
201
|
+
height = layout_file.get_attr_value(S_HEIGHT).to_f
|
|
202
|
+
if preview
|
|
203
|
+
# todo -0.5, damit die rechten und unteren Ränder zu sehen sind, jetzt sitzt die Karte aber unsymetrisch auf dem Rechteck
|
|
204
|
+
# drawing.fill("none")
|
|
205
|
+
# drawing.stroke("black")
|
|
206
|
+
drawing.rect(x: transform.x, y: transform.y, width: mm(width_with_margins(width)).round-1, height: mm(height_with_margins(height)).round-1, fill: "white", stroke: "black", "stroke-width": 1)
|
|
207
|
+
# drawing.rectangle(transform.x, transform.y, cpos.x, cpos.y)
|
|
208
|
+
# canvas.rect(mm(width_with_margins(width - 0.5)), mm(height_with_margins(height - 0.5))).styles(fill: "none", stroke: "black")
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
tra = transform.translate(mm(card_margin_left).round, mm(card_margin_top).round)
|
|
212
|
+
# Fill card background for preview
|
|
213
|
+
rx = mm(3).round
|
|
214
|
+
if preview
|
|
215
|
+
# drawing.fill("lightgray")
|
|
216
|
+
# drawing.stroke("none")
|
|
217
|
+
drawing.rect(x: tra.x, y: tra.y, width: mm(width).round, height: mm(height).round, fill: "lightgray", rx: , stroke: "blue", "stroke-width": 1)
|
|
218
|
+
# drawing = drawing.draw_rect([200,200,200], tra.x, tra.y, cpos.x-tra.x, cpos.y-tra.y, fill: true)
|
|
219
|
+
# drawing.roundrectangle(tra.x, tra.y, cpos.x, cpos.y, rx, rx)
|
|
220
|
+
# canvas1.rect(mm(width), mm(height)).round(rx).styles(fill: "lightgray", stroke: "none")
|
|
221
|
+
end
|
|
222
|
+
# die graphischen Elemnte zeichnen
|
|
223
|
+
|
|
224
|
+
# Text-Objekte
|
|
225
|
+
sorted_id_list = layout_file.sorted_id_list
|
|
226
|
+
sorted_id_list.each do |id|
|
|
227
|
+
go = layout_file.find_graphic_object_by_id(id)
|
|
228
|
+
# nur die Objekte zeichnen, die auf diese Seite sollen
|
|
229
|
+
if go[GOA_PAGE_NR].to_i == page_nr && (layers == :all || layers.include?(go[GOA_LAYER].to_i))
|
|
230
|
+
|
|
231
|
+
# die allgemeinen Attribute ermitteln
|
|
232
|
+
x = go[GOA_X].to_f
|
|
233
|
+
y = go[GOA_Y].to_f
|
|
234
|
+
go_width = go[GOA_WIDTH].to_f
|
|
235
|
+
go_height = go[GOA_HEIGHT].to_f
|
|
236
|
+
rotation = go[GOA_ROTATION].to_f
|
|
237
|
+
typ = go[GOA_TYP]
|
|
238
|
+
|
|
239
|
+
if !go[GOA_DATA]
|
|
240
|
+
data = nil
|
|
241
|
+
elsif row && (row.keys - DataMan.meta_headers).any?
|
|
242
|
+
data = DataMan.replace_variables(go[GOA_DATA], row)
|
|
243
|
+
elsif go[GOA_DATA]
|
|
244
|
+
data_nosub = go[GOA_DATA].to_s
|
|
245
|
+
data = go[GOA_SAMPLE_DATA].to_s
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
if tra
|
|
249
|
+
rec = tra.translate(mm(x).round, mm(y).round).rotate(rotation)
|
|
250
|
+
rec2 = rec.translate(mm(go_width), 0)
|
|
251
|
+
rec3 = rec.translate(mm(go_width), mm(go_height))
|
|
252
|
+
rec4 = rec.translate(0, mm(go_height))
|
|
253
|
+
orec = ObjRectangle.new(rec.vector2d, rec2.vector2d, rec3.vector2d, rec4.vector2d, id)
|
|
254
|
+
rect_list.add(orec)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
drawing.g transform: "rotate(#{rec.a})" do
|
|
258
|
+
opos = rec.inverse
|
|
259
|
+
|
|
260
|
+
begin
|
|
261
|
+
# die Objekt-spezifischen Attribute ermitteln
|
|
262
|
+
if typ == TEXTOBJECT
|
|
263
|
+
draw_textobject(drawing, opos, go, go_width, go_height, data.empty? ? data_nosub : data)
|
|
264
|
+
elsif typ == PICTUREOBJECT
|
|
265
|
+
draw_pictureobject(drawing, opos, go, go_width, go_height, data)
|
|
266
|
+
elsif typ == BARCODEOBJECT
|
|
267
|
+
data = data.gsub(/\\x([0-9a-f][0-9a-f])/i) { [$1.hex].pack("C") }
|
|
268
|
+
draw_barcode(drawing, opos, go, go_width, go_height, data, barcode_dpi)
|
|
269
|
+
elsif typ == BOXOBJECT
|
|
270
|
+
draw_boxobject(drawing, opos, go, go_width, go_height)
|
|
271
|
+
elsif typ == EMBEDOBJECT
|
|
272
|
+
draw_embedobject(drawing, opos, go, go_width, go_height, data, row ? row["row_number"] : "1", barcode_dpi)
|
|
273
|
+
end
|
|
274
|
+
rescue => err
|
|
275
|
+
raise err, "#{go[GOA_NAME]}: #{err}"
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# in Vorschau einen Rahmen um das selektierte Objekt zeichnen
|
|
279
|
+
if preview && selected_object_ids.include?(id)
|
|
280
|
+
# drawing.fill("none")
|
|
281
|
+
# drawing.stroke("red")
|
|
282
|
+
# drawing.stroke_width(1)
|
|
283
|
+
# cpos = opos.translate(mm(go_width), mm(go_height))
|
|
284
|
+
# drawing.rectangle(opos.x, opos.y, cpos.x, cpos.y)
|
|
285
|
+
drawing.rect(x: opos.x, y: opos.y, width: [mm(go_width), 0.5].max, height: [mm(go_height), 0.5].max, fill: "none", stroke: "red", "stroke-width": 1)
|
|
286
|
+
# body.rect(mm(go_width), mm(go_height)).styles(fill: "none", stroke: "red")
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
if preview
|
|
293
|
+
# paint magstripe for preview
|
|
294
|
+
if page_nr == 1 && layout_file.get_attr_value(S_MAGSTRIPE) == S_TRUE
|
|
295
|
+
rs_rect = tra.translate(mm(0), mm(4))
|
|
296
|
+
drawing.rect(x: rs_rect.x, y: rs_rect.y, width: mm(width).round, height: mm(12.5).round, fill: "black", stroke: "none", opacity: 0.5)
|
|
297
|
+
|
|
298
|
+
ms_obj = layout_file.magstripe_object
|
|
299
|
+
(1..3).each do |spurnr|
|
|
300
|
+
spur_data = ms_obj["ms_spur" + spurnr.to_s]
|
|
301
|
+
spurpos = rs_rect.translate(mm(3), mm((3.5 * spurnr) + 0.5))
|
|
302
|
+
drawing.text_("Mag #{spurnr}: #{spur_data}",
|
|
303
|
+
x: spurpos.x, y: spurpos.y,
|
|
304
|
+
"font-family": "Courier-New Bold Mono",
|
|
305
|
+
"font-size": pt(10),
|
|
306
|
+
fill: "white")
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
# paint contactless chip for preview
|
|
310
|
+
if page_nr == 0 && layout_file.get_attr_value(S_CONTACTLESS) == S_TRUE
|
|
311
|
+
clpa = tra.translate(mm(3), mm(3))
|
|
312
|
+
drawing.rect(x: clpa.x, y: clpa.y, width: mm(5), height: mm(5), rx: mm(0.5), fill: "gray", stroke: "none", opacity: 0.5)
|
|
313
|
+
clpa = tra.translate(mm(4), mm(4))
|
|
314
|
+
drawing.rect(x: clpa.x, y: clpa.y, width: mm(75), height: mm(45), rx: mm(3), fill: "none", stroke: "gray", stroke_opacity: 0.5)
|
|
315
|
+
|
|
316
|
+
clpa = tra.translate(mm(5), mm(5))
|
|
317
|
+
drawing.rect(x: clpa.x, y: clpa.y, width: mm(73), height: mm(43), rx: mm(3), fill: "none", stroke: "gray", stroke_opacity: 0.5)
|
|
318
|
+
|
|
319
|
+
clpa = tra.translate(mm(6), mm(6))
|
|
320
|
+
drawing.rect(x: clpa.x, y: clpa.y, width: mm(71), height: mm(41), rx: mm(3), fill: "none", stroke: "gray", stroke_opacity: 0.5)
|
|
321
|
+
end
|
|
322
|
+
# paint contact chip for preview
|
|
323
|
+
if page_nr == 0 && layout_file.get_attr_value(S_CONTACT) == S_TRUE
|
|
324
|
+
clpa = tra.translate(mm(8), mm(18))
|
|
325
|
+
drawing.rect(x: clpa.x, y: clpa.y, width: mm(13), height: mm(12), rx: mm(2), fill: "yellow", stroke: "gray")
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
rect_list
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def draw_textobject(drawing, tra, go, go_width, go_height, data)
|
|
334
|
+
font = go[GOA_FONT_TYP]
|
|
335
|
+
if font == "from_file"
|
|
336
|
+
font = go.layout_file.expand_path(go[GOA_FONT_FILE])
|
|
337
|
+
end
|
|
338
|
+
fontsize = go[GOA_FONT_HEIGHT].to_f
|
|
339
|
+
font_color = ["#", go[GOA_FONT_COLOR]].join
|
|
340
|
+
just = go[GOA_JUSTIFICATION]
|
|
341
|
+
if just == S_JUST_RIGHT
|
|
342
|
+
pjust = Pango::Alignment::RIGHT
|
|
343
|
+
elsif just == S_JUST_MID
|
|
344
|
+
pjust = Pango::Alignment::CENTER
|
|
345
|
+
else
|
|
346
|
+
pjust = Pango::Alignment::LEFT
|
|
347
|
+
end
|
|
348
|
+
just_block = go[GOA_JUSTIFICATION_BLOCK] == "true"
|
|
349
|
+
|
|
350
|
+
if data && !data.empty?
|
|
351
|
+
font_info = get_font_info(font, go)
|
|
352
|
+
drawing.text_(fill: font_color,
|
|
353
|
+
'text-anchor': just,
|
|
354
|
+
'font-size': pt(fontsize),
|
|
355
|
+
'font-family': font_info.font_family,
|
|
356
|
+
'font-weight': font_info.font_weight,
|
|
357
|
+
'font-style': font_info.font_style,
|
|
358
|
+
'font-stretch': font_info.font_stretch,
|
|
359
|
+
'xml:space': "preserve") do
|
|
360
|
+
|
|
361
|
+
context = pango_font_map.create_context
|
|
362
|
+
layout = Pango::Layout.new(context)
|
|
363
|
+
|
|
364
|
+
pango_font = get_pango_font(font_info, pt(fontsize))
|
|
365
|
+
layout.font_description = pango_font
|
|
366
|
+
layout.width = mm(go_width) * Pango::SCALE
|
|
367
|
+
layout.height = mm(go_height) * Pango::SCALE
|
|
368
|
+
layout.wrap = Pango::WrapMode::WORD_CHAR
|
|
369
|
+
layout.justify = just_block
|
|
370
|
+
layout.alignment = pjust
|
|
371
|
+
layout.ellipsize = Pango::EllipsizeMode::END
|
|
372
|
+
layout.text = data # set the text to render
|
|
373
|
+
|
|
374
|
+
# iterate all lines of the text box
|
|
375
|
+
iter = layout.iter
|
|
376
|
+
loop do
|
|
377
|
+
|
|
378
|
+
# Pango layout coordinates are absolute from the top-left of the layout
|
|
379
|
+
_ink_rect, logical_rect = iter.cluster_extents
|
|
380
|
+
line_x = logical_rect.x.to_f / Pango::SCALE
|
|
381
|
+
# Use the logical rectangle for reliable SVG positioning coordinates
|
|
382
|
+
line_y_baseline = iter.baseline.to_f / Pango::SCALE
|
|
383
|
+
|
|
384
|
+
run = iter.run_readonly
|
|
385
|
+
current_byte_index = iter.index
|
|
386
|
+
# for justified text the signle cluster positions matter
|
|
387
|
+
if just_block ? iter.next_cluster : iter.next_run
|
|
388
|
+
next_byte_index = iter.index
|
|
389
|
+
cluster_text = data.byteslice(current_byte_index...next_byte_index)
|
|
390
|
+
else
|
|
391
|
+
cluster_text = data.byteslice(current_byte_index .. -1)
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
if run
|
|
395
|
+
pango_item = run.item
|
|
396
|
+
if pango_item.analysis.flags & Pango::ANALYSIS_FLAG_IS_ELLIPSIS != 0
|
|
397
|
+
overflow = {fill: "red"}
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
drawing.tspan(cluster_text, x: tra.x + line_x, y: tra.y + line_y_baseline, **overflow)
|
|
402
|
+
|
|
403
|
+
break unless next_byte_index
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def draw_pictureobject(drawing, tra, go, go_width, go_height, data)
|
|
410
|
+
if data.nil? || data.empty? || (data.tr("\\", "/") =~ /\/$/)
|
|
411
|
+
# kein Bildname angegeben -> dieses Objekt ignorieren
|
|
412
|
+
elsif (path = go.layout_file.expand_path(data)) && FileTest.file?(path)
|
|
413
|
+
require "rmagick"
|
|
414
|
+
|
|
415
|
+
self_dpi = dpi
|
|
416
|
+
image_page = go[GOA_IMAGE_PAGE].to_i - 1
|
|
417
|
+
go_image = Magick::ImageList.new(path) { |img|
|
|
418
|
+
# use view DPI for vector-graphics (PDF)
|
|
419
|
+
img.density = self_dpi
|
|
420
|
+
}[image_page]
|
|
421
|
+
|
|
422
|
+
# workaround: ImageMagick uses wrong colors when inserting into RVG
|
|
423
|
+
if defined?(Magick::ColorSeparationMatteType)
|
|
424
|
+
if [Magick::ColorSeparationType, Magick::ColorSeparationMatteType].include?(go_image.image_type)
|
|
425
|
+
go_image.image_type = Magick::TrueColorMatteType
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# if object size is not given, try to determine object size based on the DPI of the image
|
|
430
|
+
im_width = (go_width == 0 && go_image.x_resolution > 0) ? go_image.columns / go_image.x_resolution * 25.4 : go_width
|
|
431
|
+
im_height = (go_height == 0 && go_image.y_resolution > 0) ? go_image.rows / go_image.y_resolution * 25.4 : go_height
|
|
432
|
+
|
|
433
|
+
aspect_ratio_img = go_image.columns.to_f / go_image.rows.to_f
|
|
434
|
+
aspect_ratio_box = im_width / im_height
|
|
435
|
+
if aspect_ratio_box > aspect_ratio_img
|
|
436
|
+
# box is wider than image -> add space left and right
|
|
437
|
+
space = im_width - aspect_ratio_img * im_height
|
|
438
|
+
im_width -= space
|
|
439
|
+
apos = tra.translate(mm(space / 2.0), 0.0)
|
|
440
|
+
|
|
441
|
+
elsif aspect_ratio_box < aspect_ratio_img
|
|
442
|
+
# image is wider than box -> add space top and bottom
|
|
443
|
+
space = im_height - im_width / aspect_ratio_img
|
|
444
|
+
im_height -= space
|
|
445
|
+
apos = tra.translate(0.0, mm(space / 2.0))
|
|
446
|
+
|
|
447
|
+
else
|
|
448
|
+
apos = tra
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
cpos = apos.translate(mm(im_width), mm(im_height))
|
|
452
|
+
# drawing.composite(apos.x.round, apos.y.round, (cpos.x - apos.x).round, (cpos.y - apos.y).round, go_image)
|
|
453
|
+
# body.image(go_image, mm(im_width), mm(im_height))
|
|
454
|
+
pngdata = go_image.to_blob do |i|
|
|
455
|
+
i.format = "PNG"
|
|
456
|
+
end
|
|
457
|
+
drawing.image(x: apos.x.round, y: apos.y.round, width: mm(im_width), height: mm(im_height), "href": "data:image/png;base64,#{[pngdata].pack("m")}")
|
|
458
|
+
else
|
|
459
|
+
raise Printer::Base::ResourceNotFoundError, I18n.t(:image_not_found, default: "Das Bild '%{path}' konnte nicht gefunden werden.", path: )
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
BARCODE_ALIAS = {
|
|
464
|
+
EAN13: :Eanx,
|
|
465
|
+
EAN8: :Eanx
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
HALIGNMENT = {
|
|
469
|
+
0 => :middle,
|
|
470
|
+
1 => :start,
|
|
471
|
+
2 => :end
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
def draw_barcode(drawing, tra, go, go_width, go_height, data, barcode_dpi)
|
|
475
|
+
return if data.nil? || data.empty?
|
|
476
|
+
|
|
477
|
+
barcode_typ = go[GOA_BARCODE_TYP]
|
|
478
|
+
option1 = go[GOA_BARCODE_OPTION_1].to_i
|
|
479
|
+
option2 = go[GOA_BARCODE_OPTION_2].to_i
|
|
480
|
+
option3 = go[GOA_BARCODE_OPTION_3].to_i
|
|
481
|
+
font = go[GOA_FONT_TYP]
|
|
482
|
+
if font == "from_file"
|
|
483
|
+
font = go.layout_file.expand_path(go[GOA_FONT_FILE])
|
|
484
|
+
end
|
|
485
|
+
fontsize = go[GOA_FONT_HEIGHT].to_f
|
|
486
|
+
font_color = ["#", go[GOA_FONT_COLOR]].join
|
|
487
|
+
|
|
488
|
+
font_info = get_font_info(font, go)
|
|
489
|
+
|
|
490
|
+
begin
|
|
491
|
+
barcode = Zint.const_get(BARCODE_ALIAS[barcode_typ.to_sym] || barcode_typ).new(value: data)
|
|
492
|
+
rescue => e
|
|
493
|
+
raise Printer::Base::InvalidBarcodeData, I18n.t(:error_generating_barcode, default: "Fehler beim Generieren des Barcodes %{data}: %{e}", data: data.inspect, e: )
|
|
494
|
+
else
|
|
495
|
+
# TODO: bar_ratio wird nicht gesetzt
|
|
496
|
+
# barcode.narrow_width = 1
|
|
497
|
+
# barcode.wide_width = bar_ratio
|
|
498
|
+
barcode.input_mode = Zint::DATA_MODE # Zint::UNICODE_MODE | Zint::ESCAPE_MODE
|
|
499
|
+
# barcode.output_options = Zint::SMALL_TEXT
|
|
500
|
+
|
|
501
|
+
# 50 is the default x-dimension for most of the 1D barcodes:
|
|
502
|
+
barcode.height = go[GOA_BARCODE_HEIGHT].to_f if go[GOA_BARCODE_HEIGHT].to_f.round(3) != 0
|
|
503
|
+
barcode.show_hrt = 0 if fontsize == 0
|
|
504
|
+
barcode.option_1 = option1
|
|
505
|
+
barcode.option_2 = option2
|
|
506
|
+
barcode.option_3 = option3
|
|
507
|
+
# barcode.fontsize = fontsize # not used in zint
|
|
508
|
+
# p [go_height, go_width, barcode.height]
|
|
509
|
+
barcode.whitespace_width = 0
|
|
510
|
+
begin
|
|
511
|
+
bcvector = barcode.to_vector
|
|
512
|
+
rescue Zint::ErrorInvalidData => err
|
|
513
|
+
raise err, "#{err.message} Barcode text: #{data.inspect}"
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
%w[EAN13 EAN8].include?(barcode_typ)
|
|
517
|
+
|
|
518
|
+
# bcvector values are always a multiple of 2 or more, but we need a multiple of 1 for to_i below.
|
|
519
|
+
# So all bcvector measurements are divided by 2 or more.
|
|
520
|
+
xgran = bcvector.width.round / barcode.width
|
|
521
|
+
ygran = bcvector.height.round / barcode.rows
|
|
522
|
+
xbars, ybars = bcvector.width / xgran, bcvector.height / ygran
|
|
523
|
+
# p vector_width: bcvector.width, vector_height: bcvector.height
|
|
524
|
+
|
|
525
|
+
optimized = (go[GOA_BARCODE_OPTIMIZED] || true).to_s == "true"
|
|
526
|
+
px_width, px_height = barcode_mm_to_px(go_width, barcode_dpi), barcode_mm_to_px(go_height, barcode_dpi)
|
|
527
|
+
xdim, ydim = px_width / xbars, px_height / ybars
|
|
528
|
+
if optimized
|
|
529
|
+
xdim = xdim.to_i
|
|
530
|
+
ydim = ydim.to_i if barcode.rows > 1 # optimize y dimension only on 2D codes
|
|
531
|
+
end
|
|
532
|
+
go_width, go_height = barcode_px_to_mm(xdim * xbars, barcode_dpi), barcode_px_to_mm(ydim * ybars, barcode_dpi)
|
|
533
|
+
px_width, px_height = mm(go_width), mm(go_height)
|
|
534
|
+
xdim = px_width / xbars / xgran
|
|
535
|
+
ydim = px_height / ybars / ygran
|
|
536
|
+
|
|
537
|
+
pathd = +""
|
|
538
|
+
bcvector.each_rectangle do |bar|
|
|
539
|
+
# p [bar.x, bar.y, bar.width, bar.height]
|
|
540
|
+
# p [xdim*bar.width, ydim*bar.height, xdim*bar.x, ydim*bar.y]
|
|
541
|
+
x = tra.x + xdim * bar.x
|
|
542
|
+
y = tra.y + ydim * bar.y
|
|
543
|
+
# drawing.rectangle(
|
|
544
|
+
# x,
|
|
545
|
+
# y,
|
|
546
|
+
# x + [xdim * bar.width - 1, 0].max,
|
|
547
|
+
# y + [ydim * bar.height - 1, 0].max
|
|
548
|
+
# )
|
|
549
|
+
# drawing.rect(x: x, y: y, width: xdim * bar.width, height: ydim * bar.height, fill: font_color, stroke: "none")
|
|
550
|
+
# body.rect([xdim * bar.width / 2 - 1, 0].max, [ydim * bar.height / 2 - 1, 0].max, xdim * bar.x / 2, ydim * bar.y / 2)
|
|
551
|
+
w = xdim * bar.width
|
|
552
|
+
h = ydim * bar.height
|
|
553
|
+
pathd << "M#{x} #{y}h#{w}v#{h}h-#{w}Z"
|
|
554
|
+
end
|
|
555
|
+
drawing.path(d: pathd, stroke: "none", fill: font_color)
|
|
556
|
+
bcvector.each_string do |str|
|
|
557
|
+
# p [str.x, str.y, str.text, str.halign, str.fsize]
|
|
558
|
+
# drawing.text_anchor(HALIGNMENT[str.halign])
|
|
559
|
+
# apos = tra.translate(xdim * str.x / xgran, )
|
|
560
|
+
# drawing.text(tra.x + xdim * str.x, tra.y + ydim * str.y, str.text)
|
|
561
|
+
# body.text(xdim * str.x / 2, ydim * str.y / 2, str.text).styles(text_anchor: HALIGNMENT[str.halign])
|
|
562
|
+
drawing.text_(str.text, x: tra.x + xdim * str.x, y: tra.y + ydim * str.y,
|
|
563
|
+
'text-anchor': HALIGNMENT[str.halign],
|
|
564
|
+
fill: font_color,
|
|
565
|
+
'font-size': pt(fontsize),
|
|
566
|
+
'font-family': font_info.font_family,
|
|
567
|
+
'font-weight': font_info.font_weight,
|
|
568
|
+
'font-style': font_info.font_style,
|
|
569
|
+
'font-stretch': font_info.font_stretch,
|
|
570
|
+
'xml:space': "preserve")
|
|
571
|
+
end
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
def barcode_mm_to_px(l, barcode_dpi)
|
|
576
|
+
barcode_dpi / 25.4 * l
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
def barcode_px_to_mm(l, barcode_dpi)
|
|
580
|
+
l / barcode_dpi * 25.4
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def draw_boxobject(drawing, tra, go, go_width, go_height)
|
|
584
|
+
corner_radius = go[GOA_CORNER_RADIUS].to_f
|
|
585
|
+
fill_color = go[GOA_FILL_COLOR]
|
|
586
|
+
border_color = go[GOA_BORDER_COLOR]
|
|
587
|
+
border_width = go[GOA_BORDER_WIDTH].to_f
|
|
588
|
+
|
|
589
|
+
drawing.rect(
|
|
590
|
+
fill: "#" + fill_color,
|
|
591
|
+
stroke: "#" + border_color,
|
|
592
|
+
"stroke-width": mm(border_width).round,
|
|
593
|
+
x: tra.x, y: tra.y, width: mm(go_width).round, height: mm(go_height).round,
|
|
594
|
+
rx: mm(corner_radius), ry: mm(corner_radius))
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def draw_embedobject(drawing, tra, go, go_width, go_height, data, row_number, barcode_dpi)
|
|
598
|
+
if data.nil? || data.empty? || (data.tr("\\", "/") =~ /\/$/)
|
|
599
|
+
# kein Layout angegeben -> dieses Objekt ignorieren
|
|
600
|
+
elsif (path = go.layout_file.expand_path(data)) && FileTest.file?(path)
|
|
601
|
+
layout_file = LayoutFile.load_from_xml(path, go.layout_file.xpath_changes)
|
|
602
|
+
|
|
603
|
+
barcode_dpi2 = layout_file.get_attr_value(S_BARCODE_DPI).to_f
|
|
604
|
+
unless ((barcode_dpi - 0.01)..(barcode_dpi + 0.01)) === barcode_dpi2
|
|
605
|
+
raise Printer::Base::DpiDoesntMatch, I18n.t(:embedded_object_dpi_differs, default: "Barcode-DPI in der Vorschau (%{document_dpi}) und %{obj} (%{embedded_obj_dpi}) sind verschieden. Bitte DPI anpassen und Barcodes neu prüfen!", document_dpi: format("%0.1f", barcode_dpi), obj: go[GOA_NAME].inspect, embedded_obj_dpi: format("%0.1f", barcode_dpi2))
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
obj_w = layout_file.get_attr_value(S_WIDTH).to_f
|
|
609
|
+
obj_h = layout_file.get_attr_value(S_HEIGHT).to_f
|
|
610
|
+
|
|
611
|
+
num_w = (go_width / obj_w).round
|
|
612
|
+
num_h = (go_height / obj_h).round
|
|
613
|
+
num_up = num_h * num_w
|
|
614
|
+
|
|
615
|
+
dataman = layout_file.build_dataman_instance
|
|
616
|
+
row_num = row_number.to_i - 1
|
|
617
|
+
|
|
618
|
+
page_nr = 0
|
|
619
|
+
num_w.times do |idx_w|
|
|
620
|
+
num_h.times do |idx_h|
|
|
621
|
+
if row_num >= 0
|
|
622
|
+
row = dataman.row(row_num * num_up + idx_h * num_w + idx_w)
|
|
623
|
+
if row
|
|
624
|
+
elpos = tra.translate(mm(idx_w * obj_w).round, mm(idx_h * obj_h).round)
|
|
625
|
+
draw(drawing, elpos, layout_file, false, false, row, page_nr, layers: [0])
|
|
626
|
+
end
|
|
627
|
+
end
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
else
|
|
631
|
+
raise Printer::Base::ResourceNotFoundError, I18n.t(:embedded_object_not_found, default: "Das Layout '%{path}' konnte nicht gefunden werden.", path: )
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def get_font_info(font, obj)
|
|
636
|
+
font_info = @@font_map[font]
|
|
637
|
+
unless font_info
|
|
638
|
+
raise Printer::Base::ResourceNotFoundError, I18n.t(:font_not_found, default: "Die Schriftart '%{font}' in Object %{obj} konnte nicht gefunden werden.", font:, obj: obj[GOA_NAME].inspect )
|
|
639
|
+
end
|
|
640
|
+
font_info
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def get_pango_font(font_info, font_size)
|
|
644
|
+
desc = Pango::FontDescription.new
|
|
645
|
+
|
|
646
|
+
# 1. Family
|
|
647
|
+
desc.family = font_info.font_family
|
|
648
|
+
# 2. Size (Convert pixels/raw user units to Pango absolute size units)
|
|
649
|
+
desc.absolute_size = font_size.to_f * Pango::SCALE
|
|
650
|
+
# 3. Weight
|
|
651
|
+
weight_str = font_info.font_weight.to_s.strip.downcase
|
|
652
|
+
desc.weight = case weight_str
|
|
653
|
+
when '100', 'thin' then Pango::Weight::THIN
|
|
654
|
+
when '200', 'ultralight' then Pango::Weight::ULTRALIGHT
|
|
655
|
+
when '300', 'light' then Pango::Weight::LIGHT
|
|
656
|
+
when '400', 'normal' then Pango::Weight::NORMAL
|
|
657
|
+
when '500', 'medium' then Pango::Weight::MEDIUM
|
|
658
|
+
when '600', 'semibold' then Pango::Weight::SEMIBOLD
|
|
659
|
+
when '700', 'bold' then Pango::Weight::BOLD
|
|
660
|
+
when '800', 'ultrabold' then Pango::Weight::ULTRABOLD
|
|
661
|
+
when '900', 'heavy' then Pango::Weight::HEAVY
|
|
662
|
+
when /\A\d+\z/ then weight_str.to_i
|
|
663
|
+
else Pango::Weight::NORMAL
|
|
664
|
+
end
|
|
665
|
+
# 4. Style
|
|
666
|
+
style_str = font_info.font_style.to_s.strip.downcase
|
|
667
|
+
desc.style = case style_str
|
|
668
|
+
when 'italic' then Pango::Style::ITALIC
|
|
669
|
+
when 'oblique' then Pango::Style::OBLIQUE
|
|
670
|
+
else Pango::Style::NORMAL
|
|
671
|
+
end
|
|
672
|
+
# 5. Stretch
|
|
673
|
+
stretch_str = font_info.font_stretch.to_s.strip.downcase.gsub('-', '_')
|
|
674
|
+
desc.stretch = case stretch_str
|
|
675
|
+
when 'ultra_condensed' then Pango::Stretch::ULTRA_CONDENSED
|
|
676
|
+
when 'extra_condensed' then Pango::Stretch::EXTRA_CONDENSED
|
|
677
|
+
when 'condensed' then Pango::Stretch::CONDENSED
|
|
678
|
+
when 'semi_condensed' then Pango::Stretch::SEMI_CONDENSED
|
|
679
|
+
when 'semi_expanded' then Pango::Stretch::SEMI_EXPANDED
|
|
680
|
+
when 'expanded' then Pango::Stretch::EXPANDED
|
|
681
|
+
when 'extra_expanded' then Pango::Stretch::EXTRA_EXPANDED
|
|
682
|
+
when 'ultra_expanded' then Pango::Stretch::ULTRA_EXPANDED
|
|
683
|
+
else Pango::Stretch::NORMAL
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
desc
|
|
687
|
+
end
|
|
688
|
+
end
|
|
689
|
+
end
|