rbpdf 1.21.2 → 1.21.4

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.
data/lib/rbpdf.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # coding: ASCII-8BIT
2
+ # frozen_string_literal: true
2
3
  #============================================================+
3
4
  # File name : rbpdf.rb
4
5
  # Begin : 2002-08-03
@@ -59,6 +60,12 @@ rescue LoadError
59
60
  # MiniMagick is not available
60
61
  end
61
62
 
63
+ begin
64
+ require 'marcel' unless Object.const_defined?(:Marcel)
65
+ rescue LoadError
66
+ # Marcel is not available
67
+ end
68
+
62
69
  unless Object.const_defined?(:MiniMagick)
63
70
  begin
64
71
  # RMagick 2.14.0
@@ -234,7 +241,7 @@ class RBPDF
234
241
  # This is the class constructor.
235
242
  # It allows to set up the page format, the orientation and
236
243
  # the measure unit used in all the methods (except for the font sizes).
237
- # @since 1.0
244
+ # [@since 1.0]
238
245
  # [@param string :orientation]
239
246
  # page orientation. Possible values are (case insensitive):
240
247
  # * P or Portrait (default)
@@ -330,13 +337,16 @@ class RBPDF
330
337
  @listindent ||= 0
331
338
  @listindentlevel ||= 0
332
339
  @lispacer ||= ""
340
+ @li_position_x = nil
333
341
 
334
342
  # bookmark
335
343
  @outlines ||= []
336
344
 
337
345
  # --- javascript and form ---
338
- @javascript ||= ''
339
- @js_objects ||= []
346
+ @javascript ||= +''
347
+ @js_objects ||= {}
348
+ @js_start_obj_id ||= 300000
349
+ @js_obj_id ||= 300000
340
350
 
341
351
  @dpi = 72.0
342
352
  @newpagegroup ||= []
@@ -394,6 +404,7 @@ class RBPDF
394
404
  @page_obj_id ||= []
395
405
  @embedded_start_obj_id ||= 100000
396
406
  @form_obj_id ||= []
407
+ @default_form_prop ||= {'lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>[255, 255, 255], 'strokeColor'=>[128, 128, 128]}
397
408
  @apxo_start_obj_id ||= 400000
398
409
  @apxo_obj_id ||= 400000
399
410
  @annotation_fonts ||= {}
@@ -444,8 +455,9 @@ class RBPDF
444
455
  if @diskcache
445
456
  @buffer ||= nil
446
457
  else
447
- @buffer ||= ''
458
+ @buffer ||= +''
448
459
  end
460
+ @tmp_buffer = nil
449
461
  @pages ||= []
450
462
  @prev_pages ||= []
451
463
  @state ||= 0
@@ -525,18 +537,9 @@ class RBPDF
525
537
  @href ||= {}
526
538
  @fontlist ||= []
527
539
  getFontsList()
528
- @fgcolor = ActiveSupport::OrderedHash.new
529
- @fgcolor['R'] = 0
530
- @fgcolor['G'] = 0
531
- @fgcolor['B'] = 0
532
- @strokecolor = ActiveSupport::OrderedHash.new
533
- @strokecolor['R'] = 0
534
- @strokecolor['G'] = 0
535
- @strokecolor['B'] = 0
536
- @bgcolor = ActiveSupport::OrderedHash.new
537
- @bgcolor['R'] = 255
538
- @bgcolor['G'] = 255
539
- @bgcolor['B'] = 255
540
+ @fgcolor = [0, 0, 0]
541
+ @strokecolor = [0, 0, 0]
542
+ @bgcolor = [255, 255, 255]
540
543
  @extgstates ||= []
541
544
 
542
545
  # user's rights
@@ -1452,7 +1455,7 @@ class RBPDF
1452
1455
  #
1453
1456
  # Defines the title of the document.
1454
1457
  # [@param string :title] The title.
1455
- # [@access public$
1458
+ # [@access public]
1456
1459
  # [@since 1.2]
1457
1460
  # [@see] SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
1458
1461
  #
@@ -2295,20 +2298,15 @@ class RBPDF
2295
2298
  if (col2 == -1) and (col3 == -1) and (col4 == -1)
2296
2299
  # Grey scale
2297
2300
  @draw_color = sprintf('%.3f G', col1 / 255.0)
2298
- @strokecolor['G'] = col1
2301
+ @strokecolor = [col1]
2299
2302
  elsif col4 == -1
2300
2303
  # RGB
2301
2304
  @draw_color = sprintf('%.3f %.3f %.3f RG', col1 / 255.0, col2 / 255.0, col3 / 255.0)
2302
- @strokecolor['R'] = col1
2303
- @strokecolor['G'] = col2
2304
- @strokecolor['B'] = col3
2305
+ @strokecolor = [col1, col2, col3]
2305
2306
  else
2306
2307
  # CMYK
2307
2308
  @draw_color = sprintf('%.3f %.3f %.3f %.3f K', col1 / 100.0, col2 / 100.0, col3 / 100.0, col4 / 100.0)
2308
- @strokecolor['C'] = col1
2309
- @strokecolor['M'] = col2
2310
- @strokecolor['Y'] = col3
2311
- @strokecolor['K'] = col4
2309
+ @strokecolor = [col1, col2, col3, col4]
2312
2310
  end
2313
2311
  if (@page>0)
2314
2312
  out(@draw_color + ' ')
@@ -2368,20 +2366,15 @@ class RBPDF
2368
2366
  if (col2 == -1) and (col3 == -1) and (col4 == -1)
2369
2367
  # Grey scale
2370
2368
  @fill_color = sprintf('%.3f g', col1 / 255.0)
2371
- @bgcolor['G'] = col1
2369
+ @bgcolor = [col1]
2372
2370
  elsif col4 == -1
2373
2371
  # RGB
2374
2372
  @fill_color = sprintf('%.3f %.3f %.3f rg', col1 / 255.0, col2 / 255.0, col3 / 255.0)
2375
- @bgcolor['R'] = col1
2376
- @bgcolor['G'] = col2
2377
- @bgcolor['B'] = col3
2373
+ @bgcolor = [col1, col2, col3]
2378
2374
  else
2379
2375
  # CMYK
2380
2376
  @fill_color = sprintf('%.3f %.3f %.3f %.3f k', col1 / 100.0, col2 / 100.0, col3 / 100.0, col4 / 100.0)
2381
- @bgcolor['C'] = col1
2382
- @bgcolor['M'] = col2
2383
- @bgcolor['Y'] = col3
2384
- @bgcolor['K'] = col4
2377
+ @bgcolor = [col1, col2, col3, col4]
2385
2378
  end
2386
2379
 
2387
2380
  @color_flag = (@fill_color != @text_color)
@@ -2458,20 +2451,16 @@ class RBPDF
2458
2451
  if (col2 == -1) and (col3 == -1) and (col4 == -1)
2459
2452
  # Grey scale
2460
2453
  @text_color = sprintf('%.3f g', col1 / 255.0)
2461
- @fgcolor['G'] = col1
2454
+ @fgcolor = [col1]
2462
2455
  elsif col4 == -1
2463
2456
  # RGB
2464
2457
  @text_color = sprintf('%.3f %.3f %.3f rg', col1 / 255.0, col2 / 255.0, col3 / 255.0)
2465
- @fgcolor['R'] = col1
2466
- @fgcolor['G'] = col2
2467
- @fgcolor['B'] = col3
2458
+ @fgcolor = [col1, col2, col3]
2459
+
2468
2460
  else
2469
2461
  # CMYK
2470
2462
  @text_color = sprintf('%.3f %.3f %.3f %.3f k', col1 / 100.0, col2 / 100.0, col3 / 100.0, col4 / 100.0)
2471
- @fgcolor['C'] = col1
2472
- @fgcolor['M'] = col2
2473
- @fgcolor['Y'] = col3
2474
- @fgcolor['K'] = col4
2463
+ @fgcolor = [col1, col2, col3, col4]
2475
2464
  end
2476
2465
  @color_flag = (@fill_color != @text_color)
2477
2466
  end
@@ -2663,7 +2652,7 @@ class RBPDF
2663
2652
  end
2664
2653
 
2665
2654
  tempstyle = style.upcase
2666
- style = ''
2655
+ style = +''
2667
2656
  # underline
2668
2657
  if tempstyle.index('U') != nil
2669
2658
  @underline = true
@@ -3464,8 +3453,8 @@ class RBPDF
3464
3453
  # [@see] Cell()
3465
3454
  #
3466
3455
  def getCellCode(w, h=0, txt='', border=0, ln=0, align='', fill=0, link=nil, stretch=0, ignore_min_height=false, calign='T', valign='M')
3467
- txt = '' if txt.nil?
3468
- rs = "" # string to be returned
3456
+ txt = +'' if txt.nil?
3457
+ rs = +"" # string to be returned
3469
3458
  txt = removeSHY(txt)
3470
3459
  if !ignore_min_height
3471
3460
  min_cell_height = @font_size * @cell_height_ratio
@@ -3547,7 +3536,7 @@ class RBPDF
3547
3536
  w = @w - @r_margin - x
3548
3537
  end
3549
3538
  end
3550
- s = '';
3539
+ s = +'';
3551
3540
  # fill and borders
3552
3541
  if (fill == 1) or (border.to_i == 1)
3553
3542
  if (fill == 1)
@@ -4025,7 +4014,9 @@ class RBPDF
4025
4014
  ccode = getCellCode(w, h, '', border, 1, '', fill, '', 0, true)
4026
4015
  @lasth = prevLastH
4027
4016
 
4028
- if (border != 0) or (fill == 1)
4017
+ if @tmp_buffer
4018
+ @tmp_buffer = "#{ccode}\n#{@tmp_buffer}"
4019
+ elsif (border != 0) or (fill == 1)
4029
4020
  if !@transfmrk[@page].nil?
4030
4021
  pagemark = @transfmrk[@page]
4031
4022
  @transfmrk[@page] += (ccode + "\n").length
@@ -4081,7 +4072,7 @@ class RBPDF
4081
4072
  # * T: top
4082
4073
  # * R: right
4083
4074
  # * B: bottom
4084
- # [@param string multicell position: 'start', 'middle', 'end'
4075
+ # [@param string multicell :position] 'start', 'middle', 'end'
4085
4076
  # [@return mixed] border mode
4086
4077
  # [@access protected]
4087
4078
  # [@since 4.4.002 (2008-12-09)]
@@ -4091,7 +4082,7 @@ class RBPDF
4091
4082
  return 1
4092
4083
  end
4093
4084
  return 0 if border == 0
4094
- cborder = ''
4085
+ cborder = +''
4095
4086
  case position
4096
4087
  when 'start'
4097
4088
  if border == 1
@@ -4686,7 +4677,7 @@ class RBPDF
4686
4677
  # [@access public]
4687
4678
  #
4688
4679
  def UTF8ArrSubString(strarr, start=0, last=strarr.size)
4689
- string = ""
4680
+ string = +""
4690
4681
  start.upto(last - 1) do |i|
4691
4682
  string << unichr(strarr[i])
4692
4683
  end
@@ -4704,7 +4695,7 @@ class RBPDF
4704
4695
  # [@since 4.5.037 (2009-04-07)]
4705
4696
  #
4706
4697
  def UniArrSubString(uniarr, start=0, last=uniarr.length)
4707
- string = ''
4698
+ string = +''
4708
4699
  start.upto(last - 1) do |i|
4709
4700
  string << uniarr[i]
4710
4701
  end
@@ -5156,7 +5147,7 @@ class RBPDF
5156
5147
  end
5157
5148
  bpc=!a['bits'].nil? ? a['bits'] : 8;
5158
5149
  #Read whole file
5159
- data='';
5150
+ data=+'';
5160
5151
  open(file,'rb') do |f|
5161
5152
  data << f.read()
5162
5153
  end
@@ -5269,9 +5260,9 @@ class RBPDF
5269
5260
  f.read(4)
5270
5261
  parms='/DecodeParms <</Predictor 15 /Colors ' + (ct == 2 ? 3 : 1).to_s + ' /BitsPerComponent ' + bpc.to_s + ' /Columns ' + w.to_s + '>>'
5271
5262
  # Scan chunks looking for palette, transparency and image data
5272
- pal = ''
5273
- trns = ''
5274
- data = ''
5263
+ pal = +''
5264
+ trns = +''
5265
+ data = +''
5275
5266
  begin
5276
5267
  n = freadint(f)
5277
5268
  type = f.read(4)
@@ -5704,7 +5695,7 @@ class RBPDF
5704
5695
  self.instance_variables.each { |val|
5705
5696
  if destroyall or ((val != '@internal_encoding') and (val != '@state') and (val != '@bufferlen') and (val != '@buffer') and (val != '@diskcache') and (val != '@sign') and (val != '@signature_data') and (val != '@signature_max_length') and (val != '@byterange_string'))
5706
5697
  if (!preserve_objcopy or (val.to_s != '@objcopy')) and !val.nil?
5707
- eval("#{val} = nil")
5698
+ instance_variable_set(val, nil)
5708
5699
  end
5709
5700
  end
5710
5701
  }
@@ -5866,7 +5857,7 @@ protected
5866
5857
  temppage = temppage.gsub(@epsmarker, '')
5867
5858
  #Page
5868
5859
  @page_obj_id[n] = newobj()
5869
- out = '<<'
5860
+ out = +'<<'
5870
5861
  out << ' /Type /Page'
5871
5862
  out << ' /Parent 1 0 R'
5872
5863
  out << ' /LastModified ' + datestring()
@@ -5956,7 +5947,7 @@ protected
5956
5947
  end
5957
5948
  #Pages root
5958
5949
  @offsets[1]=@bufferlen
5959
- out = '1 0 obj << /Type /Pages /Kids ['
5950
+ out = +'1 0 obj << /Type /Pages /Kids ['
5960
5951
  @page_obj_id.each { |page_obj|
5961
5952
  out << ' ' + page_obj.to_s + ' 0 R' unless page_obj.nil?
5962
5953
  }
@@ -5986,9 +5977,9 @@ protected
5986
5977
  #
5987
5978
  def getannotsrefs(n)
5988
5979
  unless @page_annots[n] or (@sign and @signature_data['cert_type'])
5989
- return ''
5980
+ return +''
5990
5981
  end
5991
- out = ' /Annots ['
5982
+ out = +' /Annots ['
5992
5983
  if @page_annots[n]
5993
5984
  num_annots = @page_annots[n].length
5994
5985
  0.upto(num_annots - 1) do |i|
@@ -6026,28 +6017,27 @@ protected
6026
6017
  @page_annots[n].each_with_index { |pl, key|
6027
6018
  # create annotation object for grouping radiobuttons
6028
6019
  if @radiobutton_groups[n] and @radiobutton_groups[n][pl['txt']] and @radiobutton_groups[n][pl['txt']].is_a?(Array)
6029
- annots = '<<'
6020
+ annots = +'<<'
6030
6021
  annots << ' /Type /Annot'
6031
6022
  annots << ' /Subtype /Widget'
6032
6023
  annots << ' /T ' + dataannobjstring(pl['txt'])
6033
6024
  annots << ' /FT /Btn'
6034
6025
  annots << ' /Ff 49152'
6035
6026
  annots << ' /Kids ['
6027
+ defval = nil
6036
6028
  @radiobutton_groups[n][pl['txt']].each {|data|
6037
- annots << ' ' + data['kid'] + ' 0 R'
6029
+ annots << " #{data['kid']} 0 R"
6038
6030
  if data['def'] != 'Off'
6039
6031
  defval = data['def']
6040
6032
  end
6041
6033
  }
6042
6034
  annots << ' ]'
6043
- if defval
6044
- annots << ' /V /' + defval
6045
- end
6035
+ annots << " /V /#{defval}" if defval
6046
6036
  annots << ' >>'
6047
6037
  @annot_obj_id += 1
6048
6038
  @offsets[@annot_obj_id] = @bufferlen
6049
- out(@annot_obj_id + ' 0 obj ' + annots + ' endobj')
6050
- @form_obj_id.push = @annot_obj_id
6039
+ out("#{@annot_obj_id} 0 obj #{annots} endobj")
6040
+ @form_obj_id << @annot_obj_id
6051
6041
  # store object id to be used on Parent entry of Kids
6052
6042
  @radiobutton_groups[n][pl['txt']] = @annot_obj_id
6053
6043
  end
@@ -6063,7 +6053,7 @@ protected
6063
6053
  d = pl['h'] * @k
6064
6054
  rect = sprintf('%.2f %.2f %.2f %.2f', a, b, a + c, b + d)
6065
6055
  # create new annotation object
6066
- annots = '<</Type /Annot'
6056
+ annots = +'<</Type /Annot'
6067
6057
  annots << ' /Subtype /' + pl['opt']['subtype']
6068
6058
  annots << ' /Rect [' + rect + ']'
6069
6059
  ft = ['Btn', 'Tx', 'Ch', 'Sig']
@@ -6105,7 +6095,7 @@ protected
6105
6095
  else
6106
6096
  val = pl['opt']['f'].to_i
6107
6097
  end
6108
- annots << ' /F ' + val.to_i
6098
+ annots << " /F #{val}"
6109
6099
  end
6110
6100
  # annots << ' /AP '
6111
6101
  # annots << ' /AS '
@@ -6114,24 +6104,23 @@ protected
6114
6104
  end
6115
6105
  if pl['opt']['ap']
6116
6106
  # appearance stream
6117
- annots << ' /AP << ' + pl['opt']['ap'] + ' >>'
6118
6107
  annots << ' /AP <<'
6119
6108
  if pl['opt']['ap'].is_a?(Hash)
6120
6109
  pl['opt']['ap'].each {|apmode, apdef|
6121
6110
  # apmode can be: n = normal; r = rollover; d = down
6122
6111
  annots << ' /' + apmode.upcase
6123
- if apdef.is_a?(Array)
6112
+ if apdef.is_a?(Hash)
6124
6113
  annots << ' <<'
6125
6114
  apdef.each {|apstate, stream|
6126
6115
  # reference to XObject that define the appearance for this mode-state
6127
6116
  apsobjid = putAPXObject(c, d, stream)
6128
- annots << ' /' + apstate + ' ' + apsobjid + ' 0 R'
6117
+ annots << " /#{apstate} #{apsobjid} 0 R"
6129
6118
  }
6130
6119
  annots << ' >>'
6131
6120
  else
6132
6121
  # reference to XObject that define the appearance for this mode
6133
6122
  apsobjid = putAPXObject(c, d, apdef)
6134
- annots << ' ' + apsobjid + ' 0 R'
6123
+ annots << " #{apsobjid} 0 R"
6135
6124
  end
6136
6125
  }
6137
6126
  else
@@ -6143,7 +6132,7 @@ protected
6143
6132
  annots << ' /BS <<'
6144
6133
  annots << ' /Type /Border'
6145
6134
  if !pl['opt']['bs']['w'].nil?
6146
- annots << ' /W ' + pl['opt']['bs']['w'].to_i
6135
+ annots << " /W #{pl['opt']['bs']['w']}"
6147
6136
  end
6148
6137
  bstyles = ['S', 'D', 'B', 'I', 'U']
6149
6138
  if !pl['opt']['bs']['s'].nil? and bstyles.include?(pl['opt']['bs']['s'])
@@ -6205,7 +6194,7 @@ protected
6205
6194
  if !pl['opt']['t'].nil? and pl['opt']['t'].is_a?(String)
6206
6195
  annots << ' /T ' + textannobjstring(pl['opt']['t'])
6207
6196
  end
6208
- # annots .= ' /Popup '
6197
+ # annots << ' /Popup '
6209
6198
  if !pl['opt']['ca'].nil?
6210
6199
  annots << ' /CA ' + sprintf("%.4f", pl['opt']['ca'].to_f)
6211
6200
  end
@@ -6420,9 +6409,9 @@ protected
6420
6409
  end # end MK
6421
6410
 
6422
6411
  # --- Entries for field dictionaries ---
6423
- if @radiobutton_groups[n][pl['txt']]
6412
+ if @radiobutton_groups[n] and @radiobutton_groups[n][pl['txt']]
6424
6413
  # set parent
6425
- annots << ' /Parent ' + @radiobutton_groups[n][pl['txt']] + ' 0 R'
6414
+ annots << " /Parent #{@radiobutton_groups[n][pl['txt']]} 0 R"
6426
6415
  end
6427
6416
  if pl['opt']['t'] and pl['opt']['t'].is_a?(String)
6428
6417
  annots << ' /T ' + dataannobjstring(pl['opt']['t'])
@@ -6443,7 +6432,7 @@ protected
6443
6432
  else
6444
6433
  flag = pl['opt']['ff'].to_i
6445
6434
  end
6446
- annots << ' /Ff ' + flag
6435
+ annots << " /Ff #{flag}"
6447
6436
  end
6448
6437
  if pl['opt']['maxlen']
6449
6438
  annots << ' /MaxLen ' + pl['opt']['maxlen'].to_i.to_s
@@ -6529,9 +6518,9 @@ protected
6529
6518
  @offsets[@annot_obj_id] = @bufferlen
6530
6519
  out(@annot_obj_id.to_s + ' 0 obj ' + annots + ' endobj')
6531
6520
 
6532
- if formfield and ! @radiobutton_groups[n][pl['txt']]
6521
+ if formfield and !(@radiobutton_groups[n] and @radiobutton_groups[n][pl['txt']])
6533
6522
  # store reference of form object
6534
- @form_obj_id.push = @annot_obj_id
6523
+ @form_obj_id << @annot_obj_id
6535
6524
  end
6536
6525
  }
6537
6526
  end # end for each page
@@ -6563,7 +6552,7 @@ protected
6563
6552
  rect = sprintf('%.2f %.2f', w, h)
6564
6553
  out << ' /BBox [0 0 ' + rect + ']'
6565
6554
  out << ' /Matrix [1 0 0 1 0 0]'
6566
- out << ' /Resources <</ProcSet [/PDF]>>'
6555
+ out << ' /Resources 2 0 R'
6567
6556
  out << ' /Length ' + stream.length.to_s
6568
6557
  out << ' >>'
6569
6558
  out << ' ' + getstream(stream)
@@ -6855,7 +6844,7 @@ protected
6855
6844
  subsetglyphs = subsetglyphs_tmp
6856
6845
 
6857
6846
  # build new glyf table with only used glyphs
6858
- glyf = ''
6847
+ glyf = +''
6859
6848
  glyfSize = 0
6860
6849
  # create new empty indexToLoc table
6861
6850
  newIndexToLoc = Array.new(indexToLoc.length, 0)
@@ -6871,7 +6860,7 @@ protected
6871
6860
  end
6872
6861
  }
6873
6862
  # build new loca table
6874
- loca = ''
6863
+ loca = +''
6875
6864
  if short_offset
6876
6865
  newIndexToLoc.each {|offset|
6877
6866
  loca << [offset / 2].pack('n')
@@ -6933,7 +6922,7 @@ protected
6933
6922
  table['glyf']['offset'] = offset
6934
6923
  table['glyf']['checkSum'] = getTTFtableChecksum(table['glyf']['data'], table['glyf']['length'])
6935
6924
  # rebuild font
6936
- font = ''
6925
+ font = +''
6937
6926
  font << [0x10000].pack('N') # sfnt version
6938
6927
  numTables = table.length
6939
6928
  font << [numTables].pack('n') # numTables
@@ -7074,7 +7063,7 @@ protected
7074
7063
  end
7075
7064
  }
7076
7065
  # output data
7077
- w = ''
7066
+ w = +''
7078
7067
  range.each_with_index {|ws, k|
7079
7068
  if ws and ws.uniq.length == 1
7080
7069
  # interval mode is more compact
@@ -7149,7 +7138,7 @@ protected
7149
7138
  end
7150
7139
  newobj()
7151
7140
  @font_files[file]['n'] = @n
7152
- out = '<</Length '+ font.length.to_s
7141
+ out = +'<</Length '+ font.length.to_s
7153
7142
  if compressed
7154
7143
  out << ' /Filter /FlateDecode'
7155
7144
  end
@@ -7163,7 +7152,7 @@ protected
7163
7152
  out(out)
7164
7153
  end
7165
7154
  end
7166
- @fontkeys.each do |k|
7155
+ @fontkeys.each_with_index do |k, i|
7167
7156
  #Font objects
7168
7157
  setFontSubBuffer(k, 'n', @n + 1)
7169
7158
  font = getFontBuffer(k)
@@ -7172,7 +7161,7 @@ protected
7172
7161
  if (type=='core')
7173
7162
  # standard core font
7174
7163
  obj_id = newobj()
7175
- out = '<</Type /Font'
7164
+ out = +'<</Type /Font'
7176
7165
  out << ' /Subtype /Type1'
7177
7166
  out << ' /BaseFont /' + name
7178
7167
  out << ' /Name /F' + font['i'].to_s
@@ -7181,14 +7170,14 @@ protected
7181
7170
  end
7182
7171
  if name.downcase == 'helvetica'
7183
7172
  # add default font for annotations
7184
- @annotation_fonts['helvetica'] = k
7173
+ @annotation_fonts['helvetica'] = i
7185
7174
  end
7186
7175
  out << ' >> endobj'
7187
7176
  out(out)
7188
7177
  elsif (type=='Type1' || type=='TrueType')
7189
7178
  # additional Type1 or TrueType font
7190
7179
  obj_id = newobj()
7191
- out = '<</Type /Font'
7180
+ out = +'<</Type /Font'
7192
7181
  out << ' /Subtype /' + type
7193
7182
  out << ' /BaseFont /' + name
7194
7183
  out << ' /Name /F' + font['i'].to_s
@@ -7232,9 +7221,9 @@ protected
7232
7221
  Error('Unsupported font type: ' + type)
7233
7222
  end
7234
7223
  obj_id = self.send(mtd,font)
7235
- # store object ID for current font
7236
- @font_obj_ids[k] = obj_id
7237
7224
  end
7225
+ # store object ID for current font
7226
+ @font_obj_ids[k] = obj_id
7238
7227
  end
7239
7228
  end
7240
7229
 
@@ -7248,7 +7237,7 @@ protected
7248
7237
  # [@since 1.52.0.TC005 (2005-01-05)]
7249
7238
  #
7250
7239
  def puttruetypeunicode(font)
7251
- fontname = ''
7240
+ fontname = +''
7252
7241
  if font['subset']
7253
7242
  # change name for font subsetting
7254
7243
  subtag = sprintf('%06u', font['i'])
@@ -7259,7 +7248,7 @@ protected
7259
7248
  # Type0 Font
7260
7249
  # A composite font composed of other fonts, organized hierarchically
7261
7250
  obj_id = newobj()
7262
- out = '<</Type /Font'
7251
+ out = +'<</Type /Font'
7263
7252
  out << ' /Subtype /Type0'
7264
7253
  out << ' /BaseFont /' + fontname + ''
7265
7254
  out << ' /Name /F' + font['i'].to_s
@@ -7280,7 +7269,7 @@ protected
7280
7269
  # CIDFontType2
7281
7270
  # A CIDFont whose glyph descriptions are based on TrueType font technology
7282
7271
  newobj();
7283
- out = '<</Type /Font'
7272
+ out = +'<</Type /Font'
7284
7273
  out << ' /Subtype /CIDFontType2'
7285
7274
  out << ' /BaseFont /' + fontname
7286
7275
 
@@ -7301,7 +7290,7 @@ protected
7301
7290
  # Font descriptor
7302
7291
  # A font descriptor describing the CIDFont default metrics other than its glyph widths
7303
7292
  newobj();
7304
- out = '<</Type /FontDescriptor'
7293
+ out = +'<</Type /FontDescriptor'
7305
7294
  out << ' /FontName /' + fontname
7306
7295
  font['desc'].each do |key, value|
7307
7296
  if value.is_a? Float
@@ -7339,7 +7328,7 @@ protected
7339
7328
  Error('Font file not found: ' + ctgfile)
7340
7329
  end
7341
7330
  size = File.size(fontfile)
7342
- out = '<</Length ' + size.to_s + ''
7331
+ out = +'<</Length ' + size.to_s + ''
7343
7332
  if (fontfile[-2,2] == '.z') # check file extension
7344
7333
  # Decompresses data encoded using the public-domain
7345
7334
  # zlib/deflate compression method, reproducing the
@@ -7392,7 +7381,7 @@ protected
7392
7381
  longname = name
7393
7382
  end
7394
7383
  obj_id = newobj()
7395
- out = '<</Type /Font'
7384
+ out = +'<</Type /Font'
7396
7385
  out << ' /Subtype /Type0'
7397
7386
  out << ' /BaseFont /' + longname
7398
7387
  out << ' /Name /F' + font['i'].to_s
@@ -7403,7 +7392,7 @@ protected
7403
7392
  out << ' >> endobj'
7404
7393
  out(out)
7405
7394
  newobj()
7406
- out = '<</Type /Font'
7395
+ out = +'<</Type /Font'
7407
7396
  out << ' /Subtype /CIDFontType0'
7408
7397
  out << ' /BaseFont /' + name
7409
7398
  cidinfo = '/Registry ' + datastring(font['cidinfo']['Registry'])
@@ -7440,7 +7429,7 @@ protected
7440
7429
  info = getImageBuffer(file)
7441
7430
  newobj();
7442
7431
  setImageSubBuffer(file, 'n', @n)
7443
- out = '<</Type /XObject'
7432
+ out = +'<</Type /XObject'
7444
7433
  out << ' /Subtype /Image'
7445
7434
  out << ' /Width ' + info['w'].to_s
7446
7435
  out << ' /Height ' + info['h'].to_s
@@ -7463,7 +7452,7 @@ protected
7463
7452
  out << ' ' + info['parms']
7464
7453
  end
7465
7454
  if (!info['trns'].nil? and info['trns'].kind_of?(Array))
7466
- trns='';
7455
+ trns=+'';
7467
7456
  count_info = info['trns'].length
7468
7457
  count_info.times do |i|
7469
7458
  trns << info['trns'][i].to_s + ' ' + info['trns'][i].to_s + ' '
@@ -7496,14 +7485,14 @@ protected
7496
7485
 
7497
7486
  #
7498
7487
  # Output Spot Colors Resources.
7499
- # [@access protected[
7488
+ # [@access protected]
7500
7489
  # [@since 4.0.024 (2008-09-12)]
7501
7490
  #
7502
7491
  def putspotcolors()
7503
7492
  @spot_colors.each { |name, color|
7504
7493
  newobj()
7505
7494
  @spot_colors[name]['n'] = @n
7506
- out = '[/Separation /' + name.gsub(' ', '#20')
7495
+ out = +'[/Separation /' + name.gsub(' ', '#20')
7507
7496
  out << ' /DeviceCMYK <<'
7508
7497
  out << ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]'
7509
7498
  out << ' ' + sprintf('/C1 [%.4f %.4f %.4f %.4f] ', color['c']/100.0, color['m']/100.0, color['y']/100.0, color['k']/100.0)
@@ -7518,7 +7507,7 @@ protected
7518
7507
  # [@access protected]
7519
7508
  #
7520
7509
  def putresourcedict()
7521
- out = '2 0 obj'
7510
+ out = +'2 0 obj'
7522
7511
  out << ' << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'
7523
7512
  out << ' /Font <<'
7524
7513
  @fontkeys.each do |fontkey|
@@ -7547,23 +7536,28 @@ protected
7547
7536
  end
7548
7537
  }
7549
7538
  out << ' >>'
7550
- =begin
7539
+
7551
7540
  # gradient patterns
7552
7541
  if @gradients and !@gradients.empty?
7553
7542
  out << ' /Pattern <<'
7554
- @gradients.each_with_index {|grad, id|
7555
- out << ' /p' + id + ' ' + grad['pattern'] + ' 0 R'
7556
- }
7543
+ @gradients.each_with_index do |grad, id|
7544
+ next unless grad
7545
+
7546
+ out << " /p#{id} #{grad['pattern']} 0 R"
7547
+ end
7557
7548
  out << ' >>'
7558
7549
  end
7559
7550
  # gradient shadings
7560
7551
  if @gradients and !@gradients.empty?
7561
7552
  out << ' /Shading <<'
7562
- @gradients.each_with_index {|grad, id|
7563
- out << ' /Sh' + id + ' ' + grad['id'] + ' 0 R'
7564
- }
7553
+ @gradients.each_with_index do |grad, id|
7554
+ next unless grad
7555
+
7556
+ out << " /Sh#{id} #{grad['id']} 0 R"
7557
+ end
7565
7558
  out << ' >>'
7566
7559
  end
7560
+ =begin
7567
7561
  # spot colors
7568
7562
  if @spot_colors and !@spot_colors.empty?
7569
7563
  out << ' /ColorSpace <<'
@@ -7585,16 +7579,18 @@ protected
7585
7579
  mapLinksToHtmlAnchors()
7586
7580
  putextgstates()
7587
7581
  putocg()
7588
- putfonts();
7589
- putimages();
7582
+ putfonts()
7583
+ putimages()
7590
7584
  putspotcolors()
7585
+ putshaders()
7591
7586
 
7592
7587
  #Resource dictionary
7593
7588
  @offsets[2]=@bufferlen
7594
- putresourcedict();
7589
+ putresourcedict()
7595
7590
  putbookmarks()
7596
7591
  putEmbeddedFiles()
7597
7592
  putannotsobjs()
7593
+ putjavascript()
7598
7594
  # encryption
7599
7595
 
7600
7596
  ### T.B.D ### TCPDF 5.0.000 ###
@@ -7607,7 +7603,7 @@ protected
7607
7603
  #
7608
7604
  def putinfo()
7609
7605
  newobj()
7610
- out = '<<'
7606
+ out = +'<<'
7611
7607
  if !empty_string(@title)
7612
7608
  # The document's title.
7613
7609
  out << ' /Title ' + textstring(@title)
@@ -7651,13 +7647,13 @@ protected
7651
7647
  #
7652
7648
  def putcatalog()
7653
7649
  newobj()
7654
- out = '<< /Type /Catalog'
7650
+ out = +'<< /Type /Catalog'
7655
7651
  out << ' /Pages 1 0 R'
7656
- if (@zoom_mode=='fullpage')
7652
+ if @zoom_mode == 'fullpage'
7657
7653
  out << ' /OpenAction [3 0 R /Fit]'
7658
- elsif (@zoom_mode=='fullwidth')
7654
+ elsif @zoom_mode == 'fullwidth'
7659
7655
  out << ' /OpenAction [3 0 R /FitH null]'
7660
- elsif (@zoom_mode=='real')
7656
+ elsif @zoom_mode == 'real'
7661
7657
  out << ' /OpenAction [3 0 R /XYZ null null 1]'
7662
7658
  elsif @zoom_mode.is_a?(Numeric)
7663
7659
  out << ' /OpenAction [3 0 R /XYZ null null ' + (@zoom_mode/100.0).to_s + ']'
@@ -7674,7 +7670,7 @@ protected
7674
7670
  end
7675
7671
  out << ' /Names <<'
7676
7672
  if !@javascript.empty? or !@js_objects.empty?
7677
- out << ' /JavaScript ' + @n_js + ' 0 R'
7673
+ out << " /JavaScript #{@n_js} 0 R"
7678
7674
  end
7679
7675
  out << ' >>'
7680
7676
 
@@ -7687,9 +7683,43 @@ protected
7687
7683
  v = @n_ocg_view.to_s + ' 0 R'
7688
7684
  as = '<</Event /Print /OCGs [' + p + ' ' + v + '] /Category [/Print]>> <</Event /View /OCGs [' + p + ' ' + v + '] /Category [/View]>>'
7689
7685
  out << ' /OCProperties <</OCGs [' + p + ' ' + v + '] /D <</ON [' + p + '] /OFF [' + v + '] /AS [' + as + ']>>>>'
7686
+ # AcroForm
7687
+ if !@form_obj_id.empty? || (@sign && @signature_data['cert_type'])
7688
+ out << ' /AcroForm<<'
7689
+ objrefs = +''
7690
+ if @sign && @signature_data['cert_type']
7691
+ objrefs << "#{@sig_obj_id} 0 R"
7692
+ end
7693
+ @form_obj_id.each{|objid|
7694
+ objrefs << " #{objid} 0 R"
7695
+ }
7696
+ out << " /Fields [#{objrefs}]"
7697
+ if @sign && @signature_data['cert_type']
7698
+ out << ' /SigFlags 3'
7699
+ end
7690
7700
 
7691
- ### T.B.D ### TCPDF 5.0.000 ###
7692
-
7701
+ #out << ' /CO '
7702
+ unless @annotation_fonts.empty?
7703
+ out << ' /DR <<'
7704
+ out << ' /Font <<'
7705
+ @annotation_fonts.each {|font, fontkey|
7706
+ out << " /F#{fontkey + 1} #{@font_obj_ids[font]} 0 R"
7707
+ }
7708
+ out << ' >> >>'
7709
+ end
7710
+ out << " /DA (/F#{@fontkeys.index('helvetica') + 1} 0 Tf 0 g)"
7711
+ out << ' /Q ' + (@rtl ? '2' : '0')
7712
+ #out << ' /XFA '
7713
+ out << ' >>'
7714
+ # signatures
7715
+ if @sign && @signature_data['cert_type']
7716
+ if @signature_data['cert_type'] > 0
7717
+ out << " /Perms<</DocMDP #{@sig_obj_id + 1} 0 R>>"
7718
+ else
7719
+ out << " /Perms<</UR3 #{@sig_obj_id + 1} 0 R>>"
7720
+ end
7721
+ end
7722
+ end
7693
7723
  out << ' >> endobj'
7694
7724
  out(out)
7695
7725
  end
@@ -7702,7 +7732,7 @@ protected
7702
7732
  # [@access protected]
7703
7733
  #
7704
7734
  def putviewerpreferences()
7705
- out = '/ViewerPreferences <<'
7735
+ out = +'/ViewerPreferences <<'
7706
7736
  if @rtl
7707
7737
  out << ' /Direction /R2L'
7708
7738
  else
@@ -7756,7 +7786,7 @@ protected
7756
7786
  end
7757
7787
  end
7758
7788
  if @viewer_preferences['PrintPageRange']
7759
- print_page_range_num = ''
7789
+ print_page_range_num = +''
7760
7790
  @viewer_preferences['PrintPageRange'].each { |v|
7761
7791
  print_page_range_num << ' ' + (v - 1).to_s + ''
7762
7792
  }
@@ -7775,7 +7805,7 @@ protected
7775
7805
  # [@access protected]
7776
7806
  #
7777
7807
  def puttrailer()
7778
- out = 'trailer <<'
7808
+ out = +'trailer <<'
7779
7809
  out << ' /Size ' + (@n+1).to_s
7780
7810
  out << ' /Root ' + @n.to_s + ' 0 R'
7781
7811
  out << ' /Info ' + (@n-1).to_s + ' 0 R'
@@ -7833,6 +7863,20 @@ protected
7833
7863
  out(sprintf('%010d 00000 n ', @offsets[i]))
7834
7864
  end
7835
7865
  end
7866
+ # Javascript Objects
7867
+ if @js_obj_id > @js_start_obj_id
7868
+ out((@js_start_obj_id + 1).to_s + ' ' + (@js_obj_id - @js_start_obj_id).to_s)
7869
+ (@js_start_obj_id + 1).upto(@js_obj_id) do |i|
7870
+ out(sprintf('%010d 00000 n ', @offsets[i]))
7871
+ end
7872
+ end
7873
+ # Appearance streams XObjects
7874
+ if @apxo_obj_id > @apxo_start_obj_id
7875
+ out((@apxo_start_obj_id + 1).to_s + ' ' + (@apxo_obj_id - @apxo_start_obj_id).to_s)
7876
+ (@apxo_start_obj_id + 1).upto(@apxo_obj_id) do |i|
7877
+ out(sprintf('%010d 00000 n ', @offsets[i]))
7878
+ end
7879
+ end
7836
7880
  #Trailer
7837
7881
  puttrailer();
7838
7882
  out('startxref');
@@ -7872,7 +7916,7 @@ protected
7872
7916
  #
7873
7917
  def beginpage(orientation='', format='')
7874
7918
  @page += 1;
7875
- setPageBuffer(@page, '')
7919
+ setPageBuffer(@page, +'')
7876
7920
  # initialize array for graphics tranformation positions inside a page buffer
7877
7921
  @state=2;
7878
7922
  if empty_string(orientation)
@@ -8024,7 +8068,6 @@ protected
8024
8068
  # [@access protected]
8025
8069
  #
8026
8070
  def escape(s)
8027
- # Add \ before \, ( and )
8028
8071
  s.gsub('\\','\\\\\\').gsub('(','\\(').gsub(')','\\)').gsub(13.chr, '\r')
8029
8072
  end
8030
8073
 
@@ -8108,9 +8151,9 @@ protected
8108
8151
 
8109
8152
  #
8110
8153
  # get raw output stream.
8111
- # @param string :s string to output.
8112
- # @param int :n object reference for encryption mode
8113
- # @access protected
8154
+ # [@param string :s] string to output.
8155
+ # [@param int :n] object reference for encryption mode
8156
+ # [@access protected]
8114
8157
  #
8115
8158
  def getrawstream(s, n=0)
8116
8159
  if n <= 0
@@ -8122,9 +8165,9 @@ protected
8122
8165
 
8123
8166
  #
8124
8167
  # Format output stream
8125
- # @param string :s string to output.
8126
- # @param int :n object reference for encryption mode
8127
- # @access protected
8168
+ # [@param string :s] string to output.
8169
+ # [@param int :n] object reference for encryption mode
8170
+ # [@access protected]
8128
8171
  #
8129
8172
  def getstream(s, n=0)
8130
8173
  "stream\n" + getrawstream(s, n=0) + "\nendstream"
@@ -8146,8 +8189,11 @@ protected
8146
8189
  # [@access protected]
8147
8190
  #
8148
8191
  def out(s)
8149
- s.force_encoding('ASCII-8BIT') if s.respond_to?(:force_encoding)
8150
- if (@state==2)
8192
+ s = (+s).force_encoding('ASCII-8BIT') if s.respond_to?(:force_encoding)
8193
+
8194
+ if @tmp_buffer
8195
+ @tmp_buffer << "#{s}\n"
8196
+ elsif (@state==2)
8151
8197
  if !@in_footer and !@footerlen[@page].nil? and (@footerlen[@page] > 0)
8152
8198
  # puts data before page footer
8153
8199
  pagebuff = getPageBuffer(@page)
@@ -8320,7 +8366,7 @@ protected
8320
8366
  if !@is_unicode
8321
8367
  return str # string is not in unicode
8322
8368
  end
8323
- outstr = '' # string to be returned
8369
+ outstr = +'' # string to be returned
8324
8370
  unicode = UTF8StringToArray(str) # array containing UTF-8 unicode values
8325
8371
  unicode.each {|char|
8326
8372
  if char < 256
@@ -8404,7 +8450,7 @@ protected
8404
8450
  # [@see] UTF8ToUTF16BE()
8405
8451
  #
8406
8452
  def arrUTF8ToUTF16BE(unicode, setbom=true)
8407
- outstr = ""; # string to be returned
8453
+ outstr = +""; # string to be returned
8408
8454
  if (setbom)
8409
8455
  outstr << "\xFE\xFF"; # Byte Order Mark (BOM)
8410
8456
  end
@@ -8610,7 +8656,16 @@ public
8610
8656
  # [@return array] RGB color or empty array in case of error.
8611
8657
  # [@access public]
8612
8658
  #
8613
- def convertHTMLColorToDec(color = "#FFFFFF")
8659
+ def convert_html_color_to_dec_array(color = "#FFFFFF")
8660
+ returncolor = _convert_html_color_to_dec(color)
8661
+ if returncolor.is_a? Hash
8662
+ returncolor.values
8663
+ else
8664
+ returncolor
8665
+ end
8666
+ end
8667
+
8668
+ private def _convert_html_color_to_dec(color = "#FFFFFF")
8614
8669
  color = color.gsub(/[\s]*/, '') # remove extra spaces
8615
8670
  color = color.downcase
8616
8671
  if !(dotpos = color.index('.')).nil?
@@ -8620,7 +8675,6 @@ public
8620
8675
  if color.length == 0
8621
8676
  return []
8622
8677
  end
8623
- returncolor = ActiveSupport::OrderedHash.new
8624
8678
  # RGB ARRAY
8625
8679
  if color[0,3] == 'rgb'
8626
8680
  codes = color.sub(/^rgb\(/, '')
@@ -8635,6 +8689,7 @@ public
8635
8689
  if color[0,4] == 'cmyk'
8636
8690
  codes = color.sub(/^cmyk\(/, '')
8637
8691
  codes = codes.gsub(')', '')
8692
+ returncolor = codes.split(',', 4)
8638
8693
  returncolor[0] = returncolor[0].to_i
8639
8694
  returncolor[1] = returncolor[1].to_i
8640
8695
  returncolor[2] = returncolor[2].to_i
@@ -8653,6 +8708,7 @@ public
8653
8708
  color_code = color.sub(/^#/, "")
8654
8709
  end
8655
8710
  # RGB VALUE
8711
+ returncolor = {}
8656
8712
  case color_code.length
8657
8713
  when 3
8658
8714
  # three-digit hexadecimal representation
@@ -8672,6 +8728,11 @@ public
8672
8728
  end
8673
8729
  return returncolor
8674
8730
  end
8731
+
8732
+ def convertHTMLColorToDec(color = "#FFFFFF")
8733
+ warn("#{__callee__} is deprecated, use convert_html_color_to_dec_array instead.", uplevel: 1)
8734
+ _convert_html_color_to_dec(color)
8735
+ end
8675
8736
  alias_method :convert_html_color_to_dec, :convertHTMLColorToDec
8676
8737
 
8677
8738
  #
@@ -8796,7 +8857,7 @@ public
8796
8857
  tm[5] = y - tm[0] * y - tm[1] * x
8797
8858
 
8798
8859
  # generate the transformation matrix
8799
- Transform(tm)
8860
+ transform(tm)
8800
8861
  end
8801
8862
  alias_method :rotate, :Rotate
8802
8863
 
@@ -8807,7 +8868,7 @@ public
8807
8868
  # [@since 2.1.000 (2008-01-07)]
8808
8869
  # [@see] StartTransform(), StopTransform()
8809
8870
  #
8810
- def Transform(tm)
8871
+ def transform(tm)
8811
8872
  out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', tm[0], tm[1], tm[2], tm[3], tm[4], tm[5]))
8812
8873
  # add tranformation matrix
8813
8874
  @transfmatrix[@transfmatrix_key].push 'a' => tm[0], 'b' => tm[1], 'c' => tm[2], 'd' => tm[3], 'e' => tm[4], 'f' => tm[5]
@@ -8816,7 +8877,7 @@ public
8816
8877
  @transfmrk[@page] = @pagelen[@page]
8817
8878
  end
8818
8879
  end
8819
- protected :Transform
8880
+ protected :transform
8820
8881
 
8821
8882
  # END TRANSFORMATIONS SECTION -------------------------
8822
8883
 
@@ -8893,6 +8954,7 @@ public
8893
8954
  end
8894
8955
  if !style['dash'].nil?
8895
8956
  dash = style['dash']
8957
+ phase = style['phase'].to_i
8896
8958
  dash_string = ''
8897
8959
  if dash != 0 and dash != ''
8898
8960
  if dash.is_a?(String) && dash =~ /^.+,/
@@ -8900,15 +8962,17 @@ public
8900
8962
  else
8901
8963
  tab = [dash]
8902
8964
  end
8903
- dash_string = ''
8965
+ dash_string = +''
8904
8966
  tab.each_with_index { |v, i|
8905
8967
  if i != 0
8906
8968
  dash_string << ' '
8907
8969
  end
8908
8970
  dash_string << sprintf("%.2f", v.to_f)
8909
8971
  }
8972
+ else
8973
+ phase = 0
8910
8974
  end
8911
- phase = 0
8975
+
8912
8976
  @linestyle_dash = sprintf("[%s] %.2f d", dash_string, phase)
8913
8977
  out(@linestyle_dash)
8914
8978
  end
@@ -9012,13 +9076,13 @@ public
9012
9076
  # [@param float :y1] Ordinate of first point
9013
9077
  # [@param float :x2] Abscissa of second point
9014
9078
  # [@param float :y2]] Ordinate of second point
9015
- # [@param hash :style] Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
9079
+ # [@param hash :style] Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty Hash).
9016
9080
  # [@access public]
9017
9081
  # [@since 1.0]
9018
9082
  # [@see] SetLineWidth(), SetDrawColor(), SetLineStyle()
9019
9083
  #
9020
- def Line(x1, y1, x2, y2, style=nil)
9021
- if style.is_a? Hash
9084
+ def Line(x1, y1, x2, y2, style={})
9085
+ if style.is_a?(Hash) && !style.empty?
9022
9086
  SetLineStyle(style)
9023
9087
  end
9024
9088
  outPoint(x1, y1)
@@ -9283,8 +9347,8 @@ public
9283
9347
  # * all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.
9284
9348
  # * 0 to (:np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.
9285
9349
  # If a key is not present or is null, not draws the line. Default value is default line style (empty array).
9286
- # [@param array :fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
9287
- # [@param boolean :closed if true the polygon is closes, otherwise will remain open
9350
+ # [@param array :fill_color] Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
9351
+ # [@param boolean :closed] if true the polygon is closes, otherwise will remain open
9288
9352
  # [@access public]
9289
9353
  # [@since 4.8.003 (2009-09-15)]
9290
9354
  #
@@ -10307,7 +10371,7 @@ public
10307
10371
 
10308
10372
  #
10309
10373
  # Create a bookmark PDF string.
10310
- # [@access private]
10374
+ # [@access protected]
10311
10375
  # [@author] Olivier Plathey, Nicola Asuni
10312
10376
  # [@since 2.1.002 (2008-02-12)]
10313
10377
  #
@@ -10370,165 +10434,1083 @@ public
10370
10434
  protected :putbookmarks
10371
10435
 
10372
10436
  # --- JAVASCRIPT ------------------------------------------------------
10373
- # --- FORM FIELDS -----------------------------------------------------
10374
- # --- END FORMS FIELDS ------------------------------------------------
10375
-
10376
10437
  #
10377
- # Create a new page group.
10378
- # * NOTE: call this function before calling AddPage()
10379
- # [@param int :page] starting group page (leave empty for next page).
10438
+ # Adds a javascript
10439
+ # [@param string :script] Javascript code
10380
10440
  # [@access public]
10381
- # [@since 3.0.000 (2008-03-27)]
10441
+ # [@author Johannes G�ntert, Nicola Asuni]
10442
+ # [@since 2.1.002 (2008-02-12)]
10382
10443
  #
10383
- def startPageGroup(page=0)
10384
- if !page.is_a? Integer or page.zero?
10385
- page = @page + 1
10386
- end
10387
- @newpagegroup[page] = true
10444
+ def IncludeJS(script)
10445
+ @javascript << script
10388
10446
  end
10389
- alias_method :start_page_group, :startPageGroup
10390
10447
 
10391
10448
  #
10392
- # Defines an alias for the total number of pages.
10393
- # It will be substituted as the document is closed.
10394
- # [@param string :alias] The alias.
10449
+ # Adds a javascript object and return object ID
10450
+ # [@param string :script] Javascript code
10451
+ # [@param boolean :onload] if true executes this object when opening the document
10452
+ # [@return int] internal object ID
10395
10453
  # [@access public]
10396
- # [@since 1.4]
10397
- # [@see] getAliasNbPages(), PageNo(), Footer()
10454
+ # [@author Nicola Asuni]
10455
+ # [@since 4.8.000 (2009-09-07)]
10398
10456
  #
10399
- def AliasNbPages(alias_nb ='{nb}')
10400
- @alias_nb_pages = alias_nb
10457
+ def addJavascriptObject(script, onload = false)
10458
+ @js_obj_id += 1
10459
+ @js_objects[@js_obj_id] = {'js' => script, 'onload' => onload}
10460
+ @js_obj_id
10401
10461
  end
10402
- alias_method :alias_nb_pages, :AliasNbPages
10403
10462
 
10404
10463
  #
10405
- # Returns the string alias used for the total number of pages.
10406
- # If the current font is unicode type, the returned string is surrounded by additional curly braces.
10407
- # [@return string]
10408
- # [@access public]
10409
- # [@since 4.0.018 (2008-08-08)]
10410
- # [@see] AliasNbPages(), PageNo(), Footer()
10464
+ # Create a javascript PDF string.
10465
+ # [@access protected]
10466
+ # [@author Johannes G�ntert, Nicola Asuni]
10467
+ # [@since 2.1.002 (2008-02-12)]
10411
10468
  #
10412
- def getAliasNbPages()
10413
- if (@current_font['type'] == 'TrueTypeUnicode') or (@current_font['type'] == 'cidfont0')
10414
- return '{' + @alias_nb_pages + '}'
10469
+ def putjavascript()
10470
+ return if @javascript.empty? && @js_objects.empty?
10471
+
10472
+ if @javascript.index 'this.addField'
10473
+ # @setUserRights() unless @ur
10474
+
10475
+ # the following two lines are used to avoid form fields duplication after saving
10476
+ # The addField method only works on Acrobat Writer, unless the document is signed with Adobe private key (UR3)
10477
+ jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2f,%.2f,%.2f,%.2f]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1)
10478
+ jsb = "getField('tcpdfdocsaved').value='saved';"
10479
+ @javascript = "#{jsa}\n#{@javascript}\n#{jsb}"
10480
+ end
10481
+ @n_js = newobj()
10482
+ out = +' << /Names ['
10483
+ unless @javascript.empty?
10484
+ out << " (EmbeddedJS) #{@n + 1} 0 R"
10485
+ end
10486
+ unless @js_objects.empty?
10487
+ @js_objects.each{|key, val|
10488
+ out << " (JS#{key}) #{key} 0 R" if val['onload']
10489
+ }
10490
+ end
10491
+ out << ' ] >> endobj'
10492
+ out(out)
10493
+ # default Javascript object
10494
+ unless @javascript.empty?
10495
+ newobj()
10496
+ out = +'<< /S /JavaScript'
10497
+ out << " /JS #{textstring(@javascript)}"
10498
+ out << ' >> endobj'
10499
+ out(out)
10500
+ end
10501
+ # additional Javascript objects
10502
+ unless @js_objects.empty?
10503
+ @js_objects.each {|key, val|
10504
+ @offsets[key] = @bufferlen
10505
+ out = "#{key} 0 obj\n << /S /JavaScript /JS #{textstring(val['js'])} >> endobj"
10506
+ out(out)
10507
+ }
10415
10508
  end
10416
- return @alias_nb_pages
10417
10509
  end
10418
- alias_method :get_alias_nb_pages, :getAliasNbPages
10510
+ protected :putjavascript
10419
10511
 
10420
10512
  #
10421
- # Defines an alias for the page number.
10422
- # It will be substituted as the document is closed.
10423
- # [@param string :alias] The alias.
10424
- # [@access public]
10425
- # [@since 4.5.000 (2009-01-02)]
10426
- # [@see] getAliasNbPages(), PageNo(), Footer()
10513
+ # Convert color to javascript color.
10514
+ # [@param string :color] color name or #RRGGBB
10515
+ # [@access protected]
10516
+ # [@author Denis Van Nuffelen, Nicola Asuni]
10517
+ # [@since 2.1.002 (2008-02-12)]
10427
10518
  #
10428
- def AliasNumPage(alias_num='{pnb}')
10429
- # Define an alias for total number of pages
10430
- @alias_num_page = alias_num
10519
+ def JScolor(color)
10520
+ aColors = ['transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray']
10521
+ if color[0] == '#'
10522
+ return sprintf("['RGB',%.3f,%.3f,%.3f]", color[1, 2].to_i(16) / 255, color[3, 2].to_i(16) / 255, color[5, 2].to_i(16) / 255)
10523
+ end
10524
+
10525
+ unless aColors.include? color
10526
+ Error('Invalid color: ' + color)
10527
+ end
10528
+ 'color.' + color
10431
10529
  end
10432
- alias_method :alias_num_page, :AliasNumPage
10530
+ protected :JScolor
10433
10531
 
10434
10532
  #
10435
- # Returns the string alias used for the page number.
10436
- # If the current font is unicode type, the returned string is surrounded by additional curly braces.
10437
- # [@return string]
10438
- # [@access public]
10439
- # [@since 4.5.000 (2009-01-02)]
10440
- # [@see] AliasNbPages(), PageNo(), Footer()
10533
+ # Adds a javascript form field.
10534
+ # [@param string :type] field type
10535
+ # [@param string :name] field name
10536
+ # [@param int :x] horizontal position
10537
+ # [@param int :y] vertical position
10538
+ # [@param int :w] width
10539
+ # [@param int :h] height
10540
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10541
+ # [@access protected]
10542
+ # [@author Denis Van Nuffelen, Nicola Asuni]
10543
+ # [@since 2.1.002 (2008-02-12)]
10441
10544
  #
10442
- def getAliasNumPage()
10443
- if (@current_font['type'] == 'TrueTypeUnicode') or (@current_font['type'] == 'cidfont0')
10444
- return '{' + @alias_num_page + '}'
10545
+ def addfield(type, name, x, y, w, h, prop)
10546
+ x = x - w if @rtl
10547
+
10548
+ # the followind avoid fields duplication after saving the document
10549
+ @javascript << "if(getField('tcpdfdocsaved').value != 'saved') {"
10550
+ k = @k
10551
+ @javascript << "f#{name}=this.addField('#{name}','#{type}',#{PageNo() - 1},[#{sprintf("%.2f,%.2f,%.2f,%.2f", x * k, (@h - y) * k + 1, (x + w) * k, (@h - y - h) * k + 1)}]);\n"
10552
+ @javascript << "f#{name}.textSize=#{@font_size_pt};\n"
10553
+ prop.each {|k, v|
10554
+ val = k[-5..-1] == 'Color' ? JScolor(v) : "'#{v}'"
10555
+ @javascript << "f#{name}.#{k}=#{val};\n"
10556
+ }
10557
+ if @rtl
10558
+ @x -= w
10559
+ else
10560
+ @x += w
10445
10561
  end
10446
- return @alias_num_page
10562
+ @javascript << '}'
10447
10563
  end
10448
- alias_method :get_alias_num_page, :getAliasNumPage
10564
+ protected :addfield
10565
+
10566
+ # --- FORM FIELDS -----------------------------------------------------
10449
10567
 
10450
10568
  #
10451
- # Return the current page in the group.
10452
- # [@return] current page in the group
10569
+ # Convert JavaScript form fields properties array to Annotation Properties array.
10570
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10571
+ # [@return array of annotation properties]
10572
+ # [@access protected]
10573
+ # [@author Nicola Asuni]
10574
+ # [@since 4.8.000 (2009-09-06)]
10575
+ #
10576
+ def getAnnotOptFromJSProp(prop)
10577
+ # the annotation options area already defined
10578
+ return prop['aopt'] if prop['aopt'].is_a?(Array)
10579
+
10580
+ opt = {} # value to be returned
10581
+ # alignment: Controls how the text is laid out within the text field.
10582
+ if prop['alignment']
10583
+ opt['q'] = case prop['alignment']
10584
+ when 'left'; 0
10585
+ when 'center'; 1
10586
+ when 'right'; 2
10587
+ else @rtl ? 2 : 0
10588
+ end
10589
+ end
10590
+ # lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle.
10591
+ if prop['lineWidth']
10592
+ linewidth = prop['lineWidth'].to_i
10593
+ else
10594
+ linewidth = 1
10595
+ end
10596
+ # borderStyle: The border style for a field.
10597
+ case prop['borderStyle']
10598
+ when 'border.d', 'dashed'
10599
+ opt['border'] = [0, 0, linewidth, [3, 2]]
10600
+ opt['bs'] = {'w'=>linewidth, 's'=>'D', 'd'=>[3, 2]}
10601
+ when 'border.b', 'beveled'
10602
+ opt['border'] = [0, 0, linewidth]
10603
+ opt['bs'] = {'w'=>linewidth, 's'=>'B'}
10604
+ when 'border.i', 'inset'
10605
+ opt['border'] = [0, 0, linewidth]
10606
+ opt['bs'] = {'w'=>linewidth, 's'=>'I'}
10607
+ when 'border.u', 'underline'
10608
+ opt['border'] = [0, 0, linewidth]
10609
+ opt['bs'] = {'w'=>linewidth, 's'=>'U'}
10610
+ else # 'border.s', 'solid'
10611
+ opt['border'] = [0, 0, linewidth]
10612
+ opt['bs'] = {'w'=>linewidth, 's'=>'S'}
10613
+ end
10614
+
10615
+ opt['border'] = prop['border'] if prop['border'].is_a?(Array)
10616
+ opt['mk'] ||= {}
10617
+ opt['mk']['if'] ||= {}
10618
+ opt['mk']['if']['a'] = [0.5, 0.5]
10619
+ # buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon.
10620
+ opt['mk']['if']['a'][0] = prop['buttonAlignX'] if prop['buttonAlignX']
10621
+ # buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon.
10622
+ opt['mk']['if']['a'][1] = prop['buttonAlignY'] if prop['buttonAlignY']
10623
+ # buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field.
10624
+ opt['mk']['if']['fb'] = true if prop['buttonFitBounds'] && (prop['buttonFitBounds'] == 'true')
10625
+ # buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face.
10626
+ case prop['buttonScaleHow']
10627
+ when 'scaleHow.proportional'; opt['mk']['if']['s'] = 'P'
10628
+ when 'scaleHow.anamorphic'; opt['mk']['if']['s'] = 'A'
10629
+ end
10630
+ # buttonScaleWhen: Controls when an icon is scaled to fit inside the button face.
10631
+ case prop['buttonScaleWhen']
10632
+ when 'scaleWhen.always'; opt['mk']['if']['sw'] = 'A'
10633
+ when 'scaleWhen.never'; opt['mk']['if']['sw'] = 'N'
10634
+ when 'scaleWhen.tooBig'; opt['mk']['if']['sw'] = 'B'
10635
+ when 'scaleWhen.tooSmall'; opt['mk']['if']['sw'] = 'S'
10636
+ end
10637
+ # buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face.
10638
+ case prop['buttonPosition']
10639
+ when 0, 'position.textOnly'; opt['mk']['tp'] = 0
10640
+ when 1, 'position.iconOnly'; opt['mk']['tp'] = 1
10641
+ when 2, 'position.iconTextV'; opt['mk']['tp'] = 2
10642
+ when 3, 'position.textIconV'; opt['mk']['tp'] = 3
10643
+ when 4, 'position.iconTextH'; opt['mk']['tp'] = 4
10644
+ when 5, 'position.textIconH'; opt['mk']['tp'] = 5
10645
+ when 6, 'position.overlay'; opt['mk']['tp'] = 6
10646
+ end
10647
+ # fillColor: Specifies the background color for a field.
10648
+ if prop['fillColor']
10649
+ if prop['fillColor'].is_a? Array
10650
+ opt['mk']['bg'] = prop['fillColor']
10651
+ else
10652
+ opt['mk']['bg'] = convert_html_color_to_dec_array(prop['fillColor'])
10653
+ end
10654
+ end
10655
+ # strokeColor: Specifies the stroke color for a field that is used to stroke the rectangle of the field with a line as large as the line width.
10656
+ if prop['strokeColor']
10657
+ if prop['strokeColor'].is_a? Array
10658
+ opt['mk']['bc'] = prop['strokeColor']
10659
+ else
10660
+ opt['mk']['bc'] = convert_html_color_to_dec_array(prop['strokeColor'])
10661
+ end
10662
+ end
10663
+ # rotation: The rotation of a widget in counterclockwise increments.
10664
+ opt['mk']['r'] = prop['rotation'] if prop['rotation']
10665
+ # charLimit: Limits the number of characters that a user can type into a text field.
10666
+ opt['maxlen'] = prop['charLimit'].to_i if prop['charLimit']
10667
+
10668
+ ff ||= 0
10669
+ # readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
10670
+ ff |= 1 << 0 if prop['readonly'] == 'true'
10671
+ # required: Specifies whether a field requires a value.
10672
+ ff |= 1 << 1 if prop['required'] == 'true'
10673
+ # multiline: Controls how text is wrapped within the field.
10674
+ ff |= 1 << 12 if prop['multiline'] == 'true'
10675
+ # password: Specifies whether the field should display asterisks when data is entered in the field.
10676
+ ff |= 1 << 13 if prop['password'] == 'true'
10677
+ # NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect.
10678
+ ff |= 1 << 14 if prop['NoToggleToOff'] == 'true'
10679
+ # Radio: If set, the field is a set of radio buttons.
10680
+ ff |= 1 << 15 if prop['Radio'] == 'true'
10681
+ # Pushbutton: If set, the field is a pushbutton that does not retain a permanent value.
10682
+ ff |= 1 << 16 if prop['Pushbutton'] == 'true'
10683
+ # Combo: If set, the field is a combo box; if clear, the field is a list box.
10684
+ ff |= 1 << 17 if prop['Combo'] == 'true'
10685
+ # editable: Controls whether a combo box is editable.
10686
+ ff |= 1 << 18 if prop['editable'] == 'true'
10687
+ # Sort: If set, the field's option items shall be sorted alphabetically.
10688
+ ff |= 1 << 19 if prop['Sort'] == 'true'
10689
+ # fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection).
10690
+ ff |= 1 << 20 if prop['fileSelect'] == 'true'
10691
+ # multipleSelection: If true, indicates that a list box allows a multiple selection of items.
10692
+ ff |= 1 << 21 if prop['multipleSelection'] == 'true'
10693
+ # doNotSpellCheck: If true, spell checking is not performed on this editable text field.
10694
+ ff |= 1 << 22 if prop['doNotSpellCheck'] == 'true'
10695
+ # doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field.
10696
+ ff |= 1 << 23 if prop['doNotScroll'] == 'true'
10697
+ # comb: If set to true, the field background is drawn as series of boxes (one for each character in the value of the field) and each character of the content is drawn within those boxes. The number of boxes drawn is determined from the charLimit property. It applies only to text fields. The setter will also raise if any of the following field properties are also set multiline, password, and fileSelect. A side-effect of setting this property is that the doNotScroll property is also set.
10698
+ ff |= 1 << 24 if prop['comb'] == 'true'
10699
+ # radiosInUnison: If false, even if a group of radio buttons have the same name and export value, they behave in a mutually exclusive fashion, like HTML radio buttons.
10700
+ ff |= 1 << 25 if prop['radiosInUnison'] == 'true'
10701
+ # richText: If true, the field allows rich text formatting.
10702
+ ff |= 1 << 25 if prop['richText'] == 'true'
10703
+ # commitOnSelChange: Controls whether a field value is committed after a selection change.
10704
+ ff |= 1 << 26 if prop['commitOnSelChange'] == 'true'
10705
+ opt['ff'] = ff
10706
+
10707
+ # defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset.
10708
+ opt['dv'] = prop['defaultValue'] if prop['defaultValue']
10709
+ f = 1 << 2 # default value for annotation flags
10710
+ # readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
10711
+ if prop['readonly'] == 'true'
10712
+ f |= 1 << 6
10713
+ end
10714
+ # display: Controls whether the field is hidden or visible on screen and in print.
10715
+ case prop['display']
10716
+ when 'display.visible'
10717
+ #
10718
+ when 'display.hidden'
10719
+ f |= 1 << 1
10720
+ when 'display.noPrint'
10721
+ f &= ~(1 << 2)
10722
+ when 'display.noView'
10723
+ f |= 1 << 5
10724
+ end
10725
+ opt['f'] = f
10726
+
10727
+ # currentValueIndices: Reads and writes single or multiple values of a list box or combo box.
10728
+ opt['i'] = prop['currentValueIndices'] if prop['currentValueIndices'].is_a?(Array)
10729
+ # value: The value of the field data that the user has entered.
10730
+ if prop['value']
10731
+ if prop['value'].is_a?(Array)
10732
+ opt['opt'] = []
10733
+ prop['value'].each_with_index {|v, i|
10734
+ # exportValues: An array of strings representing the export values for the field.
10735
+ if prop['exportValues'] && prop['exportValues'][i]
10736
+ opt['opt'][i] = [prop['exportValues'][i], v]
10737
+ else
10738
+ opt['opt'][i] = v
10739
+ end
10740
+ }
10741
+ else
10742
+ opt['v'] = prop['value']
10743
+ end
10744
+ end
10745
+ # richValue: This property specifies the text contents and formatting of a rich text field.
10746
+ opt['rv'] = prop['richValue'] if prop['richValue']
10747
+ # submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded).
10748
+ opt['tm'] = prop['submitName'] if prop['submitName']
10749
+ # name: Fully qualified field name.
10750
+ opt['t'] = prop['name'] if prop['name']
10751
+ # userName: The user name (short description string) of the field.
10752
+ opt['tu'] = prop['userName'] if prop['userName']
10753
+ # highlight: Defines how a button reacts when a user clicks it.
10754
+ case prop['highlight']
10755
+ when 'none', 'highlight.n'; opt['h'] = 'N'
10756
+ when 'invert', 'highlight.i'; opt['h'] = 'i'
10757
+ when 'push', 'highlight.p'; opt['h'] = 'P'
10758
+ when 'outline', 'highlight.o'; opt['h'] = 'O'
10759
+ end
10760
+ # Unsupported options:
10761
+ # - calcOrderIndex: Changes the calculation order of fields in the document.
10762
+ # - delay: Delays the redrawing of a field's appearance.
10763
+ # - defaultStyle: This property defines the default style attributes for the form field.
10764
+ # - style: Allows the user to set the glyph style of a check box or radio button.
10765
+ # - textColor, textFont, textSize
10766
+ opt
10767
+ end
10768
+ protected :getAnnotOptFromJSProp
10769
+
10770
+ #
10771
+ # Set default properties for form fields.
10772
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10453
10773
  # [@access public]
10454
- # [@since 3.0.000 (2008-03-27)]
10774
+ # [@author Nicola Asuni]
10775
+ # [@since 4.8.000 (2009-09-06)]
10455
10776
  #
10456
- def getGroupPageNo()
10457
- return @pagegroups[@currpagegroup]
10777
+ def setFormDefaultProp(prop = [])
10778
+ @default_form_prop = prop
10458
10779
  end
10459
- alias_method :get_group_page_no, :getGroupPageNo
10780
+ alias_method :set_form_default_prop, :setFormDefaultProp
10460
10781
 
10461
10782
  #
10462
- # Returns the current group page number formatted as a string.
10783
+ # Return the default properties for form fields.
10784
+ # [@return array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10463
10785
  # [@access public]
10464
- # [@since 4.3.003 (2008-11-18)]
10465
- # [@see] PaneNo(), formatPageNumber()
10786
+ # [@author Nicola Asuni]
10787
+ # [@since 4.8.000 (2009-09-06)]
10466
10788
  #
10467
- def getGroupPageNoFormatted()
10468
- return formatPageNumber(getGroupPageNo())
10789
+ def getFormDefaultProp()
10790
+ @default_form_prop
10469
10791
  end
10470
- alias_method :get_group_page_no_formatted, :getGroupPageNoFormatted
10792
+ alias_method :get_form_default_prop, :getFormDefaultProp
10471
10793
 
10472
10794
  #
10473
- # Return the alias of the current page group
10474
- # If the current font is unicode type, the returned string is surrounded by additional curly braces.
10475
- # (will be replaced by the total number of pages in this group).
10476
- # [@return] alias of the current page group
10795
+ # Creates a text field
10796
+ # [@param string :name] field name
10797
+ # [@param float :w] Width of the rectangle
10798
+ # [@param float :h] Height of the rectangle
10799
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10800
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
10801
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
10802
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
10803
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10477
10804
  # [@access public]
10478
- # [@since 3.0.000 (2008-03-27)]
10805
+ # [@author Nicola Asuni]
10806
+ # [@since 4.8.000 (2009-09-07)]
10479
10807
  #
10480
- def getPageGroupAlias()
10481
- if (@current_font['type'] == 'TrueTypeUnicode') or (@current_font['type'] == 'cidfont1')
10482
- return '{' + @currpagegroup + '}'
10808
+ def TextField(name, w, h, prop = {}, opt = {}, x = '', y = '', js = false)
10809
+ x = @x if x == ''
10810
+ y = @y if y == ''
10811
+
10812
+ if js
10813
+ addfield('text', name, x, y, w, h, prop)
10814
+ return
10815
+ end
10816
+ # get default style
10817
+ prop = getFormDefaultProp.merge prop
10818
+ # get annotation data
10819
+ popt = getAnnotOptFromJSProp(prop)
10820
+ # set default appearance stream
10821
+ font = @font_family
10822
+ fontkey = @fontkeys.index font
10823
+ unless @annotation_fonts.include? fontkey
10824
+ @annotation_fonts[font] = fontkey
10825
+ end
10826
+ fontstyle = sprintf("/F%d %.2f Tf %s", fontkey + 1, @font_size_pt, @text_color)
10827
+ popt['da'] = fontstyle
10828
+ popt['ap'] = {}
10829
+
10830
+ if opt['v'] && !empty_string(opt['v'])
10831
+ # set Appearances
10832
+ popt['ap']['n'] = +"/Tx BMC q #{fontstyle} "
10833
+ gvars = getGraphicVars()
10834
+ @h = h
10835
+ @w = w
10836
+ @t_margin = 0
10837
+ @c_margin = 0.2
10838
+
10839
+ @tmp_buffer = +''
10840
+ multi_cell(w, h, opt['v'], 0, '', 0, 0, 0.2, 0, true, 0, false, true, 0)
10841
+ popt['ap']['n'] << @tmp_buffer
10842
+ @tmp_buffer = nil
10843
+ popt['ap']['n'] << 'Q EMC'
10844
+
10845
+ # restore previous values
10846
+ setGraphicVars(gvars, true)
10847
+ else
10848
+ popt['ap']['n'] = "q BT #{fontstyle} ET Q"
10849
+ end
10850
+
10851
+ # merge options
10852
+ opt = popt.merge opt
10853
+ # remove some conflicting options
10854
+ opt.delete :bs
10855
+ # set remaining annotation data
10856
+ opt['Subtype'] = 'Widget'
10857
+ opt['ft'] = 'Tx'
10858
+ opt['t'] = name
10859
+ #
10860
+ # Additional annotation's parameters (check _putannotsobj() method):
10861
+ # opt['f']
10862
+ # opt['ap']
10863
+ # opt['as']
10864
+ # opt['bs']
10865
+ # opt['be']
10866
+ # opt['c']
10867
+ # opt['border']
10868
+ # opt['h']
10869
+ # opt['mk']
10870
+ # opt['mk']['r']
10871
+ # opt['mk']['bc']
10872
+ # opt['mk']['bg']
10873
+ # opt['mk']['ca']
10874
+ # opt['mk']['rc']
10875
+ # opt['mk']['ac']
10876
+ # opt['mk']['i']
10877
+ # opt['mk']['ri']
10878
+ # opt['mk']['ix']
10879
+ # opt['mk']['if']
10880
+ # opt['mk']['if']['sw']
10881
+ # opt['mk']['if']['s']
10882
+ # opt['mk']['if']['a']
10883
+ # opt['mk']['if']['fb']
10884
+ # opt['mk']['tp']
10885
+ # opt['tu']
10886
+ # opt['tm']
10887
+ # opt['ff']
10888
+ # opt['v']
10889
+ # opt['dv']
10890
+ # opt['a']
10891
+ # opt['aa']
10892
+ # opt['q']
10893
+ Annotation(x, y, w, h, name, opt, 0)
10894
+ if @rtl
10895
+ @x -= w
10896
+ else
10897
+ @x += w
10483
10898
  end
10484
- return @currpagegroup
10485
10899
  end
10486
- alias_method :get_page_group_alias, :getPageGroupAlias
10900
+ alias_method :text_field, :TextField
10487
10901
 
10488
10902
  #
10489
- # Return the alias for the page number on the current page group
10490
- # If the current font is unicode type, the returned string is surrounded by additional curly braces.
10491
- # (will be replaced by the total number of pages in this group).
10492
- # [@return] alias of the current page group
10903
+ # Creates a RadioButton field
10904
+ # [@param string :name] field name
10905
+ # [@param int :w] width
10906
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10907
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
10908
+ # [@param string :onvalue] value to be returned if selected.
10909
+ # [@param boolean :checked] define the initial state.
10910
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
10911
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
10912
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10493
10913
  # [@access public]
10494
- # [@since 4.5.000 (2009-01-02)]
10914
+ # [@author Nicola Asuni]
10915
+ # [@since 4.8.000 (2009-09-07)]
10495
10916
  #
10496
- def getPageNumGroupAlias()
10497
- if (@current_font['type'] == 'TrueTypeUnicode') or (@current_font['type'] == 'cidfont0')
10498
- return '{' + @currpagegroup.gsub('{nb', '{pnb') +'}'
10917
+ def RadioButton(name, w, prop = {}, opt = {}, onvalue = 'On', checked = false, x = '', y = '', js = false)
10918
+ x = @x if x == ''
10919
+ y = @y if y == ''
10920
+
10921
+ if js
10922
+ addfield('radiobutton', name, x, y, w, w, prop)
10923
+ return
10499
10924
  end
10500
- return @currpagegroup.gsub('{nb', '{pnb')
10501
- end
10502
- alias_method :get_page_num_group_alias, :getPageNumGroupAlias
10503
10925
 
10504
- #
10505
- # Format the page numbers.
10506
- # This method can be overriden for custom formats.
10507
- # [@param int :num] page number
10508
- # [@access protected]
10509
- # [@since 4.2.005 (2008-11-06)]
10510
- #
10511
- def formatPageNumber(num)
10512
- return number_with_delimiter(num, :delimiter => ",")
10926
+ onvalue = 'On' if empty_string(onvalue)
10927
+ defval = checked ? onvalue : 'Off'
10928
+ # set data for parent group
10929
+ @radiobutton_groups[@page] ||= {}
10930
+ unless @radiobutton_groups[@page][name]
10931
+ @radiobutton_groups[@page][name] = []
10932
+ @annot_obj_id += 1
10933
+ @radio_groups << @annot_obj_id
10934
+ end
10935
+ # save object ID to be added on Kids entry on parent object
10936
+ @radiobutton_groups[@page][name] << {'kid' => @annot_obj_id + 1, 'def' => defval}
10937
+ # get default style
10938
+ prop = getFormDefaultProp.merge prop
10939
+ prop['NoToggleToOff'] = 'true'
10940
+ prop['Radio'] = 'true'
10941
+ prop['borderStyle'] = 'inset'
10942
+ # get annotation data
10943
+ popt = getAnnotOptFromJSProp(prop)
10944
+ # set additional default values
10945
+ font = 'zapfdingbats'
10946
+ AddFont(font)
10947
+ fontkey = @fontkeys.index font
10948
+ unless @annotation_fonts.include? fontkey
10949
+ @annotation_fonts[font] = fontkey
10950
+ end
10951
+ fontstyle = sprintf('/F%d %.2f Tf', fontkey + 1, @font_size_pt)
10952
+ popt['da'] = "#{fontstyle} #{@text_color}"
10953
+ popt['ap'] = {}
10954
+ popt['ap']['n'] = {}
10955
+ popt['ap']['n'][onvalue] = "q #{@text_color} BT #{fontstyle} 0 0 Td (n) Tj ET Q"
10956
+ popt['ap']['n']['Off'] = "q #{@text_color} BT #{fontstyle} 0 0 Td (o) Tj ET Q"
10957
+ popt['mk'] ||= {}
10958
+ popt['mk']['ca'] = '(l)'
10959
+ # merge options
10960
+ opt = popt.merge opt
10961
+ # set remaining annotation data
10962
+ opt['Subtype'] = 'Widget'
10963
+ opt['ft'] = 'Btn'
10964
+ if checked
10965
+ opt['v'] = ["/#{onvalue}"]
10966
+ opt['as'] = onvalue
10967
+ else
10968
+ opt['as'] = 'Off'
10969
+ end
10970
+ Annotation(x, y, w, w, name, opt, 0)
10971
+ if @rtl
10972
+ @x -= w
10973
+ else
10974
+ @x += w
10975
+ end
10513
10976
  end
10514
- protected :formatPageNumber
10977
+ alias_method :radio_button, :RadioButton
10515
10978
 
10516
10979
  #
10517
- # Format the page numbers on the Table Of Content.
10518
- # This method can be overriden for custom formats.
10519
- # [@param int :num] page number
10520
- # [@access protected]
10521
- # [@since 4.5.001 (2009-01-04)]
10522
- # [@see] addTOC(), addHTMLTOC()
10980
+ # Creates a List-box field
10981
+ # [@param string :name] field name
10982
+ # [@param int :w] width
10983
+ # [@param int :h] height
10984
+ # [@param array :values] array containing the list of values.
10985
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10986
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
10987
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
10988
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
10989
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10990
+ # [@access public]
10991
+ # [@author Nicola Asuni]
10992
+ # [@since 4.8.000 (2009-09-07)]
10523
10993
  #
10524
- def formatTOCPageNumber(num)
10525
- return number_with_delimiter(num, :delimiter => ",")
10994
+ def ListBox(name, w, h, values, prop = {}, opt = {}, x = '', y = '', js = false)
10995
+ x = @x if x == ''
10996
+ y = @y if y == ''
10997
+
10998
+ if js
10999
+ addfield('listbox', name, x, y, w, h, prop)
11000
+ s = +''
11001
+ values.each {|v|
11002
+ if v.is_a?(Array)
11003
+ s << "['#{v[0]}','#{v[1]}'],"
11004
+ else
11005
+ s << "'#{v}',"
11006
+ end
11007
+ }
11008
+ @javascript << "f#{name}.setItems([#{s[0...-1]}]);\n"
11009
+ return
11010
+ end
11011
+ # get default style
11012
+ prop = getFormDefaultProp.merge prop
11013
+ # get annotation data
11014
+ popt = getAnnotOptFromJSProp(prop)
11015
+ # set additional default values
11016
+ font = @font_family
11017
+ fontkey = @fontkeys.index font
11018
+ unless @annotation_fonts.include? fontkey
11019
+ @annotation_fonts[font] = fontkey
11020
+ end
11021
+ s = +''
11022
+ values.each {|v|
11023
+ if v.is_a?(Array)
11024
+ s << "#{v[1]}\n"
11025
+ else
11026
+ s << "#{v}\n"
11027
+ end
11028
+ }
11029
+
11030
+ fontstyle = sprintf('/F%d %.2f Tf %s', fontkey + 1, @font_size_pt, @text_color)
11031
+ popt['da'] = fontstyle
11032
+ popt['ap'] = {}
11033
+ # set Appearances
11034
+ popt['ap']['n'] = +"/Tx BMC q #{fontstyle} "
11035
+ gvars = getGraphicVars()
11036
+ @h = h
11037
+ @w = w
11038
+ @t_margin = 0
11039
+ @c_margin = 0.2
11040
+
11041
+ @tmp_buffer = +''
11042
+ multi_cell(w, h, s, 0, '', 0, 0, 0.2, 0, true, 0, false, true, 0)
11043
+ popt['ap']['n'] << @tmp_buffer
11044
+ popt['ap']['n'] << 'Q EMC'
11045
+ @tmp_buffer = nil
11046
+
11047
+ # restore previous values
11048
+ setGraphicVars(gvars, true)
11049
+
11050
+ # merge options
11051
+ opt = popt.merge opt
11052
+ # set remaining annotation data
11053
+ opt['Subtype'] = 'Widget'
11054
+ opt['ft'] = 'Ch'
11055
+ opt['t'] = name
11056
+ opt['opt'] = values
11057
+ Annotation(x, y, w, h, name, opt, 0)
11058
+ if @rtl
11059
+ @x -= w
11060
+ else
11061
+ @x += w
11062
+ end
10526
11063
  end
10527
- protected :formatTOCPageNumber
11064
+ alias_method :list_box, :ListBox
10528
11065
 
10529
11066
  #
10530
- # Returns the current page number formatted as a string.
10531
- # [@access public]
11067
+ # Creates a Combo-box field
11068
+ # [@param string :name] field name
11069
+ # [@param int :w] width
11070
+ # [@param int :h] height
11071
+ # [@param array :values] array containing the list of values.
11072
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11073
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
11074
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
11075
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
11076
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11077
+ # [@access public]
11078
+ # [@author Nicola Asuni]
11079
+ # [@since 4.8.000 (2009-09-07)]
11080
+ #
11081
+ def ComboBox(name, w, h, values, prop = {}, opt = {}, x = '', y = '', js = false)
11082
+ x = @x if x == ''
11083
+ y = @y if y == ''
11084
+
11085
+ if js
11086
+ addfield('combobox', name, x, y, w, h, prop)
11087
+ s = +''
11088
+ values.each {|v|
11089
+ if v.is_a?(Array)
11090
+ s << "['#{v[0]}','#{v[1]}'],"
11091
+ else
11092
+ s << "'#{v}',"
11093
+ end
11094
+ }
11095
+ @javascript << "f#{name}.setItems([#{s[0...-1]}]);\n"
11096
+ return
11097
+ end
11098
+ # get default style
11099
+ prop = getFormDefaultProp.merge prop
11100
+ prop['Combo'] = 'true'
11101
+ # get annotation data
11102
+ popt = getAnnotOptFromJSProp(prop)
11103
+ # set additional default options
11104
+ font = @font_family
11105
+ fontkey = @fontkeys.index font
11106
+ unless @annotation_fonts.include? fontkey
11107
+ @annotation_fonts[font] = fontkey
11108
+ end
11109
+
11110
+ s = +''
11111
+ values.each {|v|
11112
+ if v.is_a?(Array)
11113
+ s << "#{v[1]}\n"
11114
+ else
11115
+ s << "#{v}\n"
11116
+ end
11117
+ }
11118
+ fontstyle = sprintf('/F%d %.2f Tf %s', fontkey + 1, @font_size_pt, @text_color)
11119
+ popt['da'] = fontstyle
11120
+ popt['ap'] = {}
11121
+ # set Appearances
11122
+ popt['ap']['n'] = +"/Tx BMC q #{fontstyle} "
11123
+ gvars = getGraphicVars()
11124
+ @h = h
11125
+ @w = w
11126
+ @t_margin = 0
11127
+ @c_margin = 0.2
11128
+
11129
+ @tmp_buffer = +''
11130
+ multi_cell(w, h, s, 0, '', 0, 0, 0.2, 0, true, 0, false, true, 0)
11131
+ popt['ap']['n'] << @tmp_buffer
11132
+ popt['ap']['n'] << 'Q EMC'
11133
+ @tmp_buffer = nil
11134
+
11135
+ # restore previous values
11136
+ setGraphicVars(gvars, true)
11137
+
11138
+ # merge options
11139
+ opt = popt.merge opt
11140
+ # set remaining annotation data
11141
+ opt['Subtype'] = 'Widget'
11142
+ opt['ft'] = 'Ch'
11143
+ opt['t'] = name
11144
+ opt['opt'] = values
11145
+ Annotation(x, y, w, h, name, opt, 0)
11146
+ if @rtl
11147
+ @x -= w
11148
+ else
11149
+ @x += w
11150
+ end
11151
+ end
11152
+ alias_method :combo_box, :ComboBox
11153
+
11154
+ #
11155
+ # Creates a CheckBox field
11156
+ # [@param string :name] field name
11157
+ # [@param int :w] width
11158
+ # [@param boolean :checked] define the initial state.
11159
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11160
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
11161
+ # [@param string :onvalue] value to be returned if selected.
11162
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
11163
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
11164
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11165
+ # [@access public]
11166
+ # [@author Nicola Asuni]
11167
+ # [@since 4.8.000 (2009-09-07)]
11168
+ #
11169
+ def CheckBox(name, w, checked=false, prop = {}, opt = {}, onvalue = 'Yes', x = '', y = '', js = false)
11170
+ x = @x if x == ''
11171
+ y = @y if y == ''
11172
+
11173
+ if js
11174
+ addfield('checkbox', name, x, y, w, w, prop)
11175
+ return
11176
+ end
11177
+
11178
+ prop['value'] ||= ['Yes']
11179
+ # get default style
11180
+ prop = getFormDefaultProp.merge prop
11181
+ prop['borderStyle'] = 'inset'
11182
+ # get annotation data
11183
+ popt = getAnnotOptFromJSProp(prop)
11184
+ # set additional default options
11185
+ font = 'zapfdingbats'
11186
+ AddFont(font)
11187
+ fontkey = @fontkeys.index font
11188
+ unless @annotation_fonts.include? fontkey
11189
+ @annotation_fonts[font] = fontkey
11190
+ end
11191
+ fontstyle = sprintf('/F%d %.2f Tf', fontkey + 1, @font_size_pt)
11192
+ popt['da'] = "#{fontstyle} #{@text_color}"
11193
+ popt['ap'] = {}
11194
+ popt['ap']['n'] = {}
11195
+ popt['ap']['n']['Yes'] = "q #{@text_color} BT #{fontstyle} 0 0 Td (n) Tj ET Q"
11196
+ popt['ap']['n']['Off'] = "q #{@text_color} BT #{fontstyle} 0 0 Td (o) Tj ET Q"
11197
+
11198
+ # merge options
11199
+ opt = popt.merge opt
11200
+ # set remaining annotation data
11201
+ opt['Subtype'] = 'Widget'
11202
+ opt['ft'] = 'Btn'
11203
+ opt['t'] = name
11204
+ if empty_string(onvalue)
11205
+ onvalue = 'Yes'
11206
+ end
11207
+ opt['opt'] = [onvalue]
11208
+ if checked
11209
+ opt['v'] = ['/Yes']
11210
+ opt['as'] = 'Yes'
11211
+ else
11212
+ opt['v'] = ['/Off']
11213
+ opt['as'] = 'Off'
11214
+ end
11215
+ Annotation(x, y, w, w, name, opt, 0)
11216
+ if @rtl
11217
+ @x -= w
11218
+ else
11219
+ @x += w
11220
+ end
11221
+ end
11222
+ alias_method :check_box, :CheckBox
11223
+
11224
+ #
11225
+ # Creates a button field
11226
+ # [@param string :name] field name
11227
+ # [@param int :w] width
11228
+ # [@param int :h] height
11229
+ # [@param string :caption] caption.
11230
+ # [@param mixed :action] action triggered by pressing the button. Use a string to specify a javascript action. Use an array to specify a form action options as on section 12.7.5 of PDF32000_2008.
11231
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11232
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
11233
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
11234
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
11235
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11236
+ # [@access public]
11237
+ # [@author Nicola Asuni]
11238
+ # [@since 4.8.000 (2009-09-07)]
11239
+ #
11240
+ def Button(name, w, h, caption, action, prop = {}, opt = {}, x = '', y = '', js = false)
11241
+ x = @x if x == ''
11242
+ y = @y if y == ''
11243
+
11244
+ if js
11245
+ addfield('button', name, x, y, w, h, prop)
11246
+ @javascript << "f#{name}.buttonSetCaption('#{caption}');\n"
11247
+ @javascript << "f#{name}.setAction('MouseUp','#{action}');\n"
11248
+ @javascript << "f#{name}.highlight='push';\n"
11249
+ @javascript << "f#{name}.print=false;\n"
11250
+ return
11251
+ end
11252
+ # get default style
11253
+ prop = getFormDefaultProp.merge prop
11254
+ prop['Pushbutton'] = 'true'
11255
+ prop['highlight'] = 'push'
11256
+ prop['display'] = 'display.noPrint'
11257
+ # get annotation data
11258
+ popt = getAnnotOptFromJSProp(prop)
11259
+ # set additional default options
11260
+ popt['mk'] ||= {}
11261
+ popt['mk']['ca'] = textstring(caption)
11262
+ popt['mk']['rc'] = textstring(caption)
11263
+ popt['mk']['ac'] = textstring(caption)
11264
+ font = @font_family
11265
+ fontkey = @fontkeys.index font
11266
+ unless @annotation_fonts.include? fontkey
11267
+ @annotation_fonts[font] = fontkey
11268
+ end
11269
+ fontstyle = sprintf('/F%d %.2f Tf %s', fontkey + 1, @font_size_pt, @text_color)
11270
+ popt['da'] = fontstyle
11271
+ popt['ap'] = {}
11272
+ # set Appearances
11273
+ popt['ap']['n'] = +"/Tx BMC q #{fontstyle} 0.800 g\n"
11274
+
11275
+ gvars = getGraphicVars()
11276
+ @h = h
11277
+ @w = w
11278
+ @c_margin *= 1.6
11279
+
11280
+ @tmp_buffer = +''
11281
+ SetLineStyle({'width' => 1.0, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => [231]})
11282
+ SetFillColor(204)
11283
+ multi_cell(w, h, caption, 1, 'C', 1, 0, 0, 0, true)
11284
+ popt['ap']['n'] << @tmp_buffer
11285
+ popt['ap']['n'] << 'Q EMC'
11286
+ @tmp_buffer = nil
11287
+
11288
+ # restore previous values
11289
+ setGraphicVars(gvars, true)
11290
+
11291
+ # merge options
11292
+ opt = popt.merge opt
11293
+ # set remaining annotation data
11294
+ opt['Subtype'] = 'Widget'
11295
+ opt['ft'] = 'Btn'
11296
+ opt['t'] = caption
11297
+ opt['v'] = name
11298
+ unless action.empty?
11299
+ if action.is_a?(Hash)
11300
+ # form action options as on section 12.7.5 of PDF32000_2008.
11301
+ opt['aa'] = +'/D <<'
11302
+ bmode = ['SubmitForm', 'ResetForm', 'ImportData']
11303
+ action.each {|key, val|
11304
+ if (key == 'S') && bmode.include?(val)
11305
+ opt['aa'] << " /S /#{val}"
11306
+ elsif (key == 'F') && !val.empty?
11307
+ opt['aa'] << " /F #{datastring(val)}"
11308
+ elsif (key == 'Fields') && val.is_a?(Array) && !val.empty?
11309
+ opt['aa'] << ' /Fields ['
11310
+ val.each {|field|
11311
+ opt['aa'] << " #{textstring(field)}"
11312
+ }
11313
+ opt['aa'] << ']'
11314
+ elsif key == 'Flags'
11315
+ ff = 0
11316
+ if val.is_a?(Array)
11317
+ val.each {|flag|
11318
+ case flag
11319
+ when 'Include/Exclude'; ff |= 1 << 0
11320
+ when 'IncludeNoValueFields'; ff |= 1 << 1
11321
+ when 'ExportFormat'; ff |= 1 << 2
11322
+ when 'GetMethod'; ff |= 1 << 3
11323
+ when 'SubmitCoordinates'; ff |= 1 << 4
11324
+ when 'XFDF'; ff |= 1 << 5
11325
+ when 'IncludeAppendSaves'; ff |= 1 << 6
11326
+ when 'IncludeAnnotations'; ff |= 1 << 7
11327
+ when 'SubmitPDF'; ff |= 1 << 8
11328
+ when 'CanonicalFormat'; ff |= 1 << 9
11329
+ when 'ExclNonUserAnnots'; ff |= 1 << 10
11330
+ when 'ExclFKey'; ff |= 1 << 11
11331
+ when 'EmbedForm'; ff |= 1 << 13
11332
+ end
11333
+ }
11334
+ else
11335
+ ff = val.to_i
11336
+ end
11337
+ opt['aa'] << " /Flags #{ff}"
11338
+ end
11339
+ }
11340
+ opt['aa'] << ' >>'
11341
+ else
11342
+ # Javascript action or raw action command
11343
+ js_obj_id = addJavascriptObject(action)
11344
+ opt['aa'] = "/D #{js_obj_id} 0 R"
11345
+ end
11346
+ end
11347
+ Annotation(x, y, w, h, name, opt, 0)
11348
+ if @rtl
11349
+ @x -= w
11350
+ else
11351
+ @x += w
11352
+ end
11353
+ end
11354
+ alias_method :button, :Button
11355
+
11356
+ # --- END FORMS FIELDS ------------------------------------------------
11357
+
11358
+ #
11359
+ # Create a new page group.
11360
+ # * NOTE: call this function before calling AddPage()
11361
+ # [@param int :page] starting group page (leave empty for next page).
11362
+ # [@access public]
11363
+ # [@since 3.0.000 (2008-03-27)]
11364
+ #
11365
+ def startPageGroup(page=0)
11366
+ if !page.is_a? Integer or page.zero?
11367
+ page = @page + 1
11368
+ end
11369
+ @newpagegroup[page] = true
11370
+ end
11371
+ alias_method :start_page_group, :startPageGroup
11372
+
11373
+ #
11374
+ # Defines an alias for the total number of pages.
11375
+ # It will be substituted as the document is closed.
11376
+ # [@param string :alias] The alias.
11377
+ # [@access public]
11378
+ # [@since 1.4]
11379
+ # [@see] getAliasNbPages(), PageNo(), Footer()
11380
+ #
11381
+ def AliasNbPages(alias_nb ='{nb}')
11382
+ @alias_nb_pages = alias_nb
11383
+ end
11384
+ alias_method :alias_nb_pages, :AliasNbPages
11385
+
11386
+ #
11387
+ # Returns the string alias used for the total number of pages.
11388
+ # If the current font is unicode type, the returned string is surrounded by additional curly braces.
11389
+ # [@return string]
11390
+ # [@access public]
11391
+ # [@since 4.0.018 (2008-08-08)]
11392
+ # [@see] AliasNbPages(), PageNo(), Footer()
11393
+ #
11394
+ def getAliasNbPages()
11395
+ if (@current_font['type'] == 'TrueTypeUnicode') or (@current_font['type'] == 'cidfont0')
11396
+ return '{' + @alias_nb_pages + '}'
11397
+ end
11398
+ return @alias_nb_pages
11399
+ end
11400
+ alias_method :get_alias_nb_pages, :getAliasNbPages
11401
+
11402
+ #
11403
+ # Defines an alias for the page number.
11404
+ # It will be substituted as the document is closed.
11405
+ # [@param string :alias] The alias.
11406
+ # [@access public]
11407
+ # [@since 4.5.000 (2009-01-02)]
11408
+ # [@see] getAliasNbPages(), PageNo(), Footer()
11409
+ #
11410
+ def AliasNumPage(alias_num='{pnb}')
11411
+ # Define an alias for total number of pages
11412
+ @alias_num_page = alias_num
11413
+ end
11414
+ alias_method :alias_num_page, :AliasNumPage
11415
+
11416
+ #
11417
+ # Returns the string alias used for the page number.
11418
+ # If the current font is unicode type, the returned string is surrounded by additional curly braces.
11419
+ # [@return string]
11420
+ # [@access public]
11421
+ # [@since 4.5.000 (2009-01-02)]
11422
+ # [@see] AliasNbPages(), PageNo(), Footer()
11423
+ #
11424
+ def getAliasNumPage()
11425
+ if (@current_font['type'] == 'TrueTypeUnicode') or (@current_font['type'] == 'cidfont0')
11426
+ return '{' + @alias_num_page + '}'
11427
+ end
11428
+ return @alias_num_page
11429
+ end
11430
+ alias_method :get_alias_num_page, :getAliasNumPage
11431
+
11432
+ #
11433
+ # Return the current page in the group.
11434
+ # [@return] current page in the group
11435
+ # [@access public]
11436
+ # [@since 3.0.000 (2008-03-27)]
11437
+ #
11438
+ def getGroupPageNo()
11439
+ return @pagegroups[@currpagegroup]
11440
+ end
11441
+ alias_method :get_group_page_no, :getGroupPageNo
11442
+
11443
+ #
11444
+ # Returns the current group page number formatted as a string.
11445
+ # [@access public]
11446
+ # [@since 4.3.003 (2008-11-18)]
11447
+ # [@see] PaneNo(), formatPageNumber()
11448
+ #
11449
+ def getGroupPageNoFormatted()
11450
+ return formatPageNumber(getGroupPageNo())
11451
+ end
11452
+ alias_method :get_group_page_no_formatted, :getGroupPageNoFormatted
11453
+
11454
+ #
11455
+ # Return the alias of the current page group
11456
+ # If the current font is unicode type, the returned string is surrounded by additional curly braces.
11457
+ # (will be replaced by the total number of pages in this group).
11458
+ # [@return] alias of the current page group
11459
+ # [@access public]
11460
+ # [@since 3.0.000 (2008-03-27)]
11461
+ #
11462
+ def getPageGroupAlias()
11463
+ if (@current_font['type'] == 'TrueTypeUnicode') or (@current_font['type'] == 'cidfont1')
11464
+ return '{' + @currpagegroup + '}'
11465
+ end
11466
+ return @currpagegroup
11467
+ end
11468
+ alias_method :get_page_group_alias, :getPageGroupAlias
11469
+
11470
+ #
11471
+ # Return the alias for the page number on the current page group
11472
+ # If the current font is unicode type, the returned string is surrounded by additional curly braces.
11473
+ # (will be replaced by the total number of pages in this group).
11474
+ # [@return] alias of the current page group
11475
+ # [@access public]
11476
+ # [@since 4.5.000 (2009-01-02)]
11477
+ #
11478
+ def getPageNumGroupAlias()
11479
+ if (@current_font['type'] == 'TrueTypeUnicode') or (@current_font['type'] == 'cidfont0')
11480
+ return '{' + @currpagegroup.gsub('{nb', '{pnb') +'}'
11481
+ end
11482
+ return @currpagegroup.gsub('{nb', '{pnb')
11483
+ end
11484
+ alias_method :get_page_num_group_alias, :getPageNumGroupAlias
11485
+
11486
+ #
11487
+ # Format the page numbers.
11488
+ # This method can be overriden for custom formats.
11489
+ # [@param int :num] page number
11490
+ # [@access protected]
11491
+ # [@since 4.2.005 (2008-11-06)]
11492
+ #
11493
+ def formatPageNumber(num)
11494
+ return number_with_delimiter(num, :delimiter => ",")
11495
+ end
11496
+ protected :formatPageNumber
11497
+
11498
+ #
11499
+ # Format the page numbers on the Table Of Content.
11500
+ # This method can be overriden for custom formats.
11501
+ # [@param int :num] page number
11502
+ # [@access protected]
11503
+ # [@since 4.5.001 (2009-01-04)]
11504
+ # [@see] addTOC(), addHTMLTOC()
11505
+ #
11506
+ def formatTOCPageNumber(num)
11507
+ return number_with_delimiter(num, :delimiter => ",")
11508
+ end
11509
+ protected :formatTOCPageNumber
11510
+
11511
+ #
11512
+ # Returns the current page number formatted as a string.
11513
+ # [@access public]
10532
11514
  # [@since 4.2.005 (2008-11-06)]
10533
11515
  # [@see] PaneNo(), formatPageNumber()
10534
11516
  #
@@ -10627,7 +11609,7 @@ public
10627
11609
  newobj()
10628
11610
  @extgstates[i] ||= {}
10629
11611
  @extgstates[i]['n'] = @n
10630
- out = '<< /Type /ExtGState'
11612
+ out = +'<< /Type /ExtGState'
10631
11613
  if @extgstates[i]['parms']
10632
11614
  @extgstates[i]['parms'].each {|k, v|
10633
11615
  if v.is_a? Float
@@ -10761,6 +11743,434 @@ public
10761
11743
  end
10762
11744
  alias_method :set_viewer_preferences, :setViewerPreferences
10763
11745
 
11746
+ #
11747
+ # Paints a linear colour gradient.
11748
+ # [@param float :x] abscissa of the top left corner of the rectangle.
11749
+ # [@param float :y] ordinate of the top left corner of the rectangle.
11750
+ # [@param float :w] width of the rectangle.
11751
+ # [@param float :h] height of the rectangle.
11752
+ # [@param array :col1] first color (Grayscale, RGB or CMYK components).
11753
+ # [@param array :col2] second color (Grayscale, RGB or CMYK components).
11754
+ # [@param array :coords] array of the form (x1, y1, x2, y2) which defines the gradient vector (see linear_gradient_coords.jpg). The default value is from left to right (x1=0, y1=0, x2=1, y2=0).
11755
+ # [@since 3.1.000 (2008-06-09)]
11756
+ # [@access public]
11757
+ #
11758
+ def LinearGradient(x, y, w, h, col1=[], col2=[], coords=[0,0,1,0])
11759
+ Clip(x, y, w, h)
11760
+ Gradient(2, coords, [{'color' => col1, 'offset' => 0, 'exponent' => 1}, {'color' => col2, 'offset' => 1, 'exponent' => 1}], [], false)
11761
+ end
11762
+ alias_method :linear_gradient, :LinearGradient
11763
+
11764
+ #
11765
+ # Paints a radial colour gradient.
11766
+ # [@param float :x] abscissa of the top left corner of the rectangle.
11767
+ # [@param float :y] ordinate of the top left corner of the rectangle.
11768
+ # [@param float :w] width of the rectangle.
11769
+ # [@param float :h] height of the rectangle.
11770
+ # [@param array :col1] first color (Grayscale, RGB or CMYK components).
11771
+ # [@param array :col2] second color (Grayscale, RGB or CMYK components).
11772
+ # [@param array :coords] array of the form (fx, fy, cx, cy, r) where (fx, fy) is the starting point of the gradient with color1, (cx, cy) is the center of the circle with color2, and r is the radius of the circle (see radial_gradient_coords.jpg). (fx, fy) should be inside the circle, otherwise some areas will not be defined.
11773
+ # [@since 3.1.000 (2008-06-09)]
11774
+ # [@access public]
11775
+ #
11776
+ def RadialGradient(x, y, w, h, col1=[], col2=[], coords=[0.5,0.5,0.5,0.5,1])
11777
+ Clip(x, y, w, h)
11778
+ Gradient(3, coords, [{'color' => col1, 'offset' => 0, 'exponent' => 1}, {'color' => col2, 'offset' => 1, 'exponent' => 1}], [], false)
11779
+ end
11780
+ alias_method :radial_gradient, :RadialGradient
11781
+
11782
+ #
11783
+ # Paints a coons patch mesh.
11784
+ # [@param float :x] abscissa of the top left corner of the rectangle.
11785
+ # [@param float :y] ordinate of the top left corner of the rectangle.
11786
+ # [@param float :w] width of the rectangle.
11787
+ # [@param float :h] height of the rectangle.
11788
+ # [@param array :col1] first color (lower left corner) (RGB components).
11789
+ # [@param array :col2] second color (lower right corner) (RGB components).
11790
+ # [@param array :col3] third color (upper right corner) (RGB components).
11791
+ # [@param array :col4] fourth color (upper left corner) (RGB components).
11792
+ # [@param array :coords] <ul><li>for one patch mesh: array(float x1, float y1, .... float x12, float y12): 12 pairs of coordinates (normally from 0 to 1) which specify the Bezier control points that define the patch. First pair is the lower left edge point, next is its right control point (control point 2). Then the other points are defined in the order: control point 1, edge point, control point 2 going counter-clockwise around the patch. Last (x12, y12) is the first edge point's left control point (control point 1).</li><li>for two or more patch meshes: array[number of patches]: arrays with the following keys for each patch: f: where to put that patch (0 = first patch, 1, 2, 3 = right, top and left of precedent patch - I didn't figure this out completely - just try and error ;-) points: 12 pairs of coordinates of the Bezier control points as above for the first patch, 8 pairs of coordinates for the following patches, ignoring the coordinates already defined by the precedent patch (I also didn't figure out the order of these - also: try and see what's happening) colors: must be 4 colors for the first patch, 2 colors for the following patches</li></ul>
11793
+ # [@param array :coords_min] minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0
11794
+ # [@param array :coords_max] maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1
11795
+ # [@param boolean :antialias] A flag indicating whether to filter the shading function to prevent aliasing artifacts.
11796
+ # [@since 3.1.000 (2008-06-09)]
11797
+ # [@access public]
11798
+ #
11799
+ def CoonsPatchMesh(x, y, w, h, col1=[], col2=[], col3=[], col4=[], coords=[0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33], coords_min=0, coords_max=1, antialias=false)
11800
+ Clip(x, y, w, h)
11801
+ n = @gradients.size + 1
11802
+ @gradients[n] = {}
11803
+ @gradients[n]['type'] = 6 #coons patch mesh
11804
+ @gradients[n]['coords'] = []
11805
+ @gradients[n]['antialias'] = antialias
11806
+ @gradients[n]['colors'] = []
11807
+ @gradients[n]['transparency'] = false
11808
+ # check the coords array if it is the simple array or the multi patch array
11809
+ if coords[0].is_a? Hash
11810
+ # multi patch array
11811
+ patch_array = coords
11812
+ else
11813
+ # simple array -> convert to multi patch array
11814
+ if col1[1].nil?
11815
+ col1[1] = col1[2] = col1[0]
11816
+ end
11817
+ if col2[1].nil?
11818
+ col2[1] = col2[2] = col2[0]
11819
+ end
11820
+ if col3[1].nil?
11821
+ col3[1] = col3[2] = col3[0]
11822
+ end
11823
+ if col4[1].nil?
11824
+ col4[1] = col4[2] = col4[0]
11825
+ end
11826
+ patch_array = []
11827
+ patch_array[0] = {}
11828
+ patch_array[0]['f'] = 0
11829
+ patch_array[0]['points'] = coords
11830
+ patch_array[0]['colors'] = []
11831
+ patch_array[0]['colors'][0] = {}
11832
+ patch_array[0]['colors'][0]['r'] = col1[0]
11833
+ patch_array[0]['colors'][0]['g'] = col1[1]
11834
+ patch_array[0]['colors'][0]['b'] = col1[2]
11835
+ patch_array[0]['colors'][1] = {}
11836
+ patch_array[0]['colors'][1]['r'] = col2[0]
11837
+ patch_array[0]['colors'][1]['g'] = col2[1]
11838
+ patch_array[0]['colors'][1]['b'] = col2[2]
11839
+ patch_array[0]['colors'][2] = {}
11840
+ patch_array[0]['colors'][2]['r'] = col3[0]
11841
+ patch_array[0]['colors'][2]['g'] = col3[1]
11842
+ patch_array[0]['colors'][2]['b'] = col3[2]
11843
+ patch_array[0]['colors'][3] = {}
11844
+ patch_array[0]['colors'][3]['r'] = col4[0]
11845
+ patch_array[0]['colors'][3]['g'] = col4[1]
11846
+ patch_array[0]['colors'][3]['b'] = col4[2]
11847
+ end
11848
+ bpcd = 65535 #16 bits per coordinate
11849
+ # build the data stream
11850
+ @gradients[n]['stream'] = +''
11851
+ count_patch = patch_array.size
11852
+ count_patch.times do |i|
11853
+ @gradients[n]['stream'] << (patch_array[i]['f']).chr # start with the edge flag as 8 bit
11854
+ count_points = patch_array[i]['points'].size
11855
+ count_points.times do |j|
11856
+ # each point as 16 bit
11857
+ patch_array[i]['points'][j] = ((patch_array[i]['points'][j] - coords_min) / (coords_max - coords_min)) * bpcd
11858
+ if patch_array[i]['points'][j] < 0
11859
+ patch_array[i]['points'][j] = 0
11860
+ end
11861
+ if patch_array[i]['points'][j] > bpcd
11862
+ patch_array[i]['points'][j] = bpcd
11863
+ end
11864
+ @gradients[n]['stream'] << ((patch_array[i]['points'][j] / 256.0).floor).chr
11865
+ @gradients[n]['stream'] << ((patch_array[i]['points'][j] % 256.0).floor).chr
11866
+ end
11867
+ count_cols = patch_array[i]['colors'].size
11868
+ count_cols.times do |j|
11869
+ # each color component as 8 bit
11870
+ @gradients[n]['stream'] << (patch_array[i]['colors'][j]['r']).chr
11871
+ @gradients[n]['stream'] << (patch_array[i]['colors'][j]['g']).chr
11872
+ @gradients[n]['stream'] << (patch_array[i]['colors'][j]['b']).chr
11873
+ end
11874
+ end
11875
+ # paint the gradient
11876
+ out("/Sh#{n} sh")
11877
+ # restore previous Graphic State
11878
+ out('Q')
11879
+ end
11880
+ alias_method :coons_patch_mesh, :CoonsPatchMesh
11881
+
11882
+ #
11883
+ # Set a rectangular clipping area.
11884
+ # [@param float :x] abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
11885
+ # [@param float :y] ordinate of the top left corner of the rectangle.
11886
+ # [@param float :w] width of the rectangle.
11887
+ # [@param float :h] height of the rectangle.
11888
+ # [@since 3.1.000 (2008-06-09)]
11889
+ # [@access protected]
11890
+ #
11891
+ def Clip(x, y, w, h)
11892
+ x = @w - x - w if @rtl
11893
+
11894
+ # save current Graphic State
11895
+ s = +'q'
11896
+ # set clipping area
11897
+ s << sprintf(' %.2f %.2f %.2f %.2f re W n', x * @k, (@h - y) * @k, w * @k, -h * @k)
11898
+ # set up transformation matrix for gradient
11899
+ s << sprintf(' %.3f 0 0 %.3f %.3f %.3f cm', w * @k, h * @k, x * @k, (@h - (y + h)) * @k)
11900
+ out(s)
11901
+ end
11902
+ protected :Clip
11903
+
11904
+ #
11905
+ # Output gradient.
11906
+ # [@param int :type] type of gradient (1 Function-based shading; 2 Axial shading; 3 Radial shading; 4 Free-form Gouraud-shaded triangle mesh; 5 Lattice-form Gouraud-shaded triangle mesh; 6 Coons patch mesh; 7 Tensor-product patch mesh). (Not all types are currently supported)
11907
+ # [@param array :coords] array of coordinates.
11908
+ # [@param array :stops] array gradient color components: color = array of GRAY, RGB or CMYK color components; offset = (0 to 1) represents a location along the gradient vector; exponent = exponent of the exponential interpolation function (default = 1).
11909
+ # [@param array :background] An array of colour components appropriate to the colour space, specifying a single background colour value.
11910
+ # [@param boolean :antialias] A flag indicating whether to filter the shading function to prevent aliasing artifacts.
11911
+ # [@since 3.1.000 (2008-06-09)]
11912
+ # [@access public]
11913
+ #
11914
+ def Gradient(type, coords, stops, background=[], antialias=false)
11915
+ n = @gradients.size + 1
11916
+ @gradients[n] = {}
11917
+ @gradients[n]['type'] = type
11918
+ @gradients[n]['coords'] = coords
11919
+ @gradients[n]['antialias'] = antialias
11920
+ @gradients[n]['colors'] = []
11921
+ @gradients[n]['transparency'] = false
11922
+ # color space
11923
+ numcolspace = stops[0]['color'].size
11924
+ bcolor = background
11925
+ case numcolspace
11926
+ when 4 # CMYK
11927
+ @gradients[n]['colspace'] = 'DeviceCMYK'
11928
+ unless background.empty?
11929
+ @gradients[n]['background'] = sprintf('%.3f %.3f %.3f %.3f', bcolor[0]/100.0, bcolor[1]/100.0, bcolor[2]/100.0, bcolor[3]/100.0)
11930
+ end
11931
+ when 3 # RGB
11932
+ @gradients[n]['colspace'] = 'DeviceRGB'
11933
+ unless background.empty?
11934
+ @gradients[n]['background'] = sprintf('%.3f %.3f %.3f', bcolor[0]/255.0, bcolor[1]/255.0, bcolor[2]/255.0)
11935
+ end
11936
+ when 1 # Gray scale
11937
+ @gradients[n]['colspace'] = 'DeviceGray'
11938
+ unless background.empty?
11939
+ @gradients[n]['background'] = sprintf('%.3f', bcolor[0]/255.0)
11940
+ end
11941
+ end
11942
+ num_stops = stops.size
11943
+ last_stop_id = num_stops - 1
11944
+ stops.each_with_index do |stop, key|
11945
+ @gradients[n]['colors'][key] = {}
11946
+ # offset represents a location along the gradient vector
11947
+ if stop['offset']
11948
+ @gradients[n]['colors'][key]['offset'] = stop['offset']
11949
+ else
11950
+ if key == 0
11951
+ @gradients[n]['colors'][key]['offset'] = 0
11952
+ elsif key == last_stop_id
11953
+ @gradients[n]['colors'][key]['offset'] = 1
11954
+ else
11955
+ offsetstep = (1 - @gradients[n]['colors'][key - 1]['offset']) / (num_stops - key)
11956
+ @gradients[n]['colors'][key]['offset'] = @gradients[n]['colors'][key - 1]['offset'] + offsetstep
11957
+ end
11958
+ end
11959
+ if stop['opacity']
11960
+ @gradients[n]['colors'][key]['opacity'] = stop['opacity']
11961
+ if stop['opacity'] < 1
11962
+ @gradients[n]['transparency'] = true
11963
+ end
11964
+ else
11965
+ @gradients[n]['colors'][key]['opacity'] = 1
11966
+ end
11967
+ # exponent for the exponential interpolation function
11968
+ if stop['exponent']
11969
+ @gradients[n]['colors'][key]['exponent'] = stop['exponent']
11970
+ else
11971
+ @gradients[n]['colors'][key]['exponent'] = 1
11972
+ end
11973
+ # set colors
11974
+ color = stop['color']
11975
+ case numcolspace
11976
+ when 4 # CMYK
11977
+ @gradients[n]['colors'][key]['color'] = sprintf('%.3f %.3f %.3f %.3f', color[0]/100.0, color[1]/100.0, color[2]/100.0, color[3]/100.0)
11978
+ when 3 # RGB
11979
+ @gradients[n]['colors'][key]['color'] = sprintf('%.3f %.3f %.3f', color[0]/255.0, color[1]/255.0, color[2]/255.0)
11980
+ when 1 # Gray scale
11981
+ @gradients[n]['colors'][key]['color'] = sprintf('%.3f', color[0]/255.0)
11982
+ end
11983
+ end
11984
+ if @gradients[n]['transparency']
11985
+ # paint luminosity gradient
11986
+ out("/TGS#{n} gs")
11987
+ end
11988
+ # paint the gradient
11989
+ out("/Sh#{n} sh")
11990
+ # restore previous Graphic State
11991
+ out('Q')
11992
+ end
11993
+ alias_method :gradient, :Gradient
11994
+
11995
+ #
11996
+ # Output gradient shaders.
11997
+ # [@since 3.1.000 (2008-06-09)]
11998
+ # [@access protected]
11999
+ #
12000
+ def putshaders()
12001
+ idt = @gradients.size #index for transparency gradients
12002
+ @gradients.each_with_index do |grad, id|
12003
+ next unless grad
12004
+
12005
+ if (grad['type'] == 2) || (grad['type'] == 3)
12006
+ newobj()
12007
+ fc = @n
12008
+ out = +'<<'
12009
+ out << ' /FunctionType 3'
12010
+ out << ' /Domain [0 1]'
12011
+ functions = +''
12012
+ bounds = +''
12013
+ encode = +''
12014
+ i = 1
12015
+ num_cols = grad['colors'].size
12016
+ lastcols = num_cols - 1
12017
+ 1.upto(num_cols - 1) do |i|
12018
+ functions << "#{fc + i} 0 R "
12019
+ if i < lastcols
12020
+ bounds << sprintf('%.3f ', grad['colors'][i]['offset'])
12021
+ end
12022
+ encode << '0 1 '
12023
+ end
12024
+ out << " /Functions [#{functions.strip}]"
12025
+ out << " /Bounds [#{bounds.strip}]"
12026
+ out << " /Encode [#{encode.strip}]"
12027
+ out << ' >>'
12028
+ out << ' endobj'
12029
+ out(out)
12030
+ 1.upto(num_cols - 1) do |i|
12031
+ newobj()
12032
+ out = +'<<'
12033
+ out << ' /FunctionType 2'
12034
+ out << ' /Domain [0 1]'
12035
+ out << " /C0 [#{grad['colors'][i - 1]['color']}]"
12036
+ out << " /C1 [#{grad['colors'][i]['color']}]"
12037
+ out << " /N #{grad['colors'][i]['exponent']}"
12038
+ out << ' >>'
12039
+ out << ' endobj'
12040
+ out(out)
12041
+ end
12042
+ # set transparency fuctions
12043
+ if grad['transparency']
12044
+ newobj()
12045
+ ft = @n
12046
+ out = +'<<'
12047
+ out << ' /FunctionType 3'
12048
+ out << ' /Domain [0 1]'
12049
+ functions = +''
12050
+ i = 1
12051
+ num_cols = grad['colors'].size
12052
+ 1.upto(num_cols - 1) do |i|
12053
+ functions << "#{ft + i} 0 R "
12054
+ end
12055
+ out << " /Functions [#{functions.strip}]"
12056
+ out << " /Bounds [#{bounds.strip}]"
12057
+ out << " /Encode [#{encode.strip}]"
12058
+ out << ' >>'
12059
+ out << ' endobj'
12060
+ out(out)
12061
+ 1.upto(num_cols - 1) do |i|
12062
+ newobj()
12063
+ out = +'<<'
12064
+ out << ' /FunctionType 2'
12065
+ out << ' /Domain [0 1]'
12066
+ out << " /C0 [#{grad['colors'][(i - 1)]['opacity']}]"
12067
+ out << " /C1 [#{grad['colors'][i]['opacity']}]"
12068
+ out << " /N #{grad['colors'][i]['exponent']}"
12069
+ out << ' >>'
12070
+ out << ' endobj'
12071
+ out(out)
12072
+ end
12073
+ end
12074
+ end
12075
+ # set shading object
12076
+ newobj()
12077
+ out = +"<< /ShadingType #{grad['type']}"
12078
+ if grad['colspace']
12079
+ out << " /ColorSpace /#{grad['colspace']}"
12080
+ else
12081
+ out << ' /ColorSpace /DeviceRGB'
12082
+ end
12083
+ if grad['background'] && !grad['background'].empty?
12084
+ out << " /Background [#{grad['background']}]"
12085
+ end
12086
+ if grad['antialias'] == true
12087
+ out << ' /AntiAlias true'
12088
+ end
12089
+ case grad['type']
12090
+ when 2
12091
+ out << sprintf(' /Coords [%.3f %.3f %.3f %.3f]', grad['coords'][0], grad['coords'][1], grad['coords'][2], grad['coords'][3])
12092
+ out << ' /Domain [0 1]'
12093
+ out << " /Function #{fc} 0 R"
12094
+ out << ' /Extend [true true]'
12095
+ out << ' >>'
12096
+ when 3
12097
+ # x0, y0, r0, x1, y1, r1
12098
+ # at this this time radius of inner circle is 0
12099
+ out << sprintf(' /Coords [%.3f %.3f 0 %.3f %.3f %.3f]', grad['coords'][0], grad['coords'][1], grad['coords'][2], grad['coords'][3], grad['coords'][4])
12100
+ out << ' /Domain [0 1]'
12101
+ out << " /Function #{fc} 0 R"
12102
+ out << ' /Extend [true true]'
12103
+ out << ' >>'
12104
+ when 6
12105
+ out << ' /BitsPerCoordinate 16'
12106
+ out << ' /BitsPerComponent 8'
12107
+ out << ' /Decode[0 1 0 1 0 1 0 1 0 1]'
12108
+ out << ' /BitsPerFlag 8'
12109
+ out << " /Length #{grad['stream'].length} >> "
12110
+ out << getstream(grad['stream'])
12111
+ end
12112
+ out << ' endobj'
12113
+ out(out)
12114
+ if grad['transparency']
12115
+ shading_transparency = out.gsub(%r{/ColorSpace /[^\s]+}mi, '/ColorSpace /DeviceGray')
12116
+ shading_transparency = shading_transparency.gsub(%r{/Function [0-9]+ }mi, "/Function #{ft} ")
12117
+ end
12118
+ @gradients[id]['id'] = @n
12119
+ # set pattern object
12120
+ newobj()
12121
+ out = +'<< /Type /Pattern /PatternType 2'
12122
+ out << " /Shading #{@gradients[id]['id']} 0 R"
12123
+ out << ' >> endobj'
12124
+ out(out)
12125
+ @gradients[id]['pattern'] = @n
12126
+ # set shading and pattern for transparency mask
12127
+ if grad['transparency']
12128
+ # luminosity pattern
12129
+ idgs = id + idt
12130
+ newobj()
12131
+ out(shading_transparency)
12132
+ @gradients[idgs] = {}
12133
+ @gradients[idgs]['id'] = @n
12134
+ newobj()
12135
+ out = +'<< /Type /Pattern /PatternType 2'
12136
+ out << " /Shading #{@gradients[idgs]['id']} 0 R"
12137
+ out << ' >> endobj'
12138
+ out(out)
12139
+ @gradients[idgs]['pattern'] = @n
12140
+ # luminosity XObject
12141
+ newobj()
12142
+ filter = ''
12143
+ stream = "q /a0 gs /Pattern cs /p#{idgs} scn 0 0 #{@w_pt} #{@h_pt} re f Q"
12144
+ if @compress
12145
+ filter = ' /Filter /FlateDecode'
12146
+ stream = Zlib::Deflate.deflate(stream)
12147
+ end
12148
+ out = +"<< /Type /XObject /Subtype /Form /FormType 1#{filter}"
12149
+ out << " /Length #{stream.length}"
12150
+ out << " /BBox [0 0 #{@w_pt} #{@h_pt}]"
12151
+ out << ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>'
12152
+ out << ' /Resources <<'
12153
+ out << ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>'
12154
+ out << " /Pattern << /p#{idgs} #{@gradients[idgs]['pattern']} 0 R >>"
12155
+ out << ' >>'
12156
+ out << ' >> '
12157
+ out << getstream(stream)
12158
+ out << ' endobj'
12159
+ out(out)
12160
+ # SMask
12161
+ newobj()
12162
+ out = +"<< /Type /Mask /S /Luminosity /G #{@n - 1} 0 R >> endobj"
12163
+ out(out)
12164
+ # ExtGState
12165
+ newobj()
12166
+ out = +"<< /Type /ExtGState /SMask #{@n - 1} 0 R /AIS false >> endobj"
12167
+ out(out)
12168
+ @extgstates << {'n' => @n, 'name' => "TGS#{id}"}
12169
+ end
12170
+ end
12171
+ end
12172
+ protected :putshaders
12173
+
10764
12174
  #
10765
12175
  # Draw the sector of a circle.
10766
12176
  # It can be used for instance to render pie charts.
@@ -11112,7 +12522,6 @@ protected
11112
12522
  if dom[key]['attribute'] and dom[key]['attribute']['class'] and !dom[key]['attribute']['class'].empty?
11113
12523
  selector_class = dom[key]['attribute']['class'].downcase
11114
12524
  end
11115
- id = ''
11116
12525
  if dom[key]['attribute'] and dom[key]['attribute']['id'] and !dom[key]['attribute']['id'].empty?
11117
12526
  selector_id = dom[key]['attribute']['id'].downcase
11118
12527
  end
@@ -11229,7 +12638,7 @@ protected
11229
12638
  # [@since 5.1.000 (2010-05-25)]
11230
12639
  #
11231
12640
  def getTagStyleFromCSS(dom, key, css)
11232
- tagstyle = '' # style to be returned
12641
+ tagstyle = +'' # style to be returned
11233
12642
  # get all styles that apply
11234
12643
  css.each { |selector, style|
11235
12644
  # remove specificity
@@ -11299,7 +12708,7 @@ protected
11299
12708
  uri.split('/').each {|path|
11300
12709
  uri_path = uri_path.join(path)
11301
12710
  }
11302
- cssdata = ''
12711
+ cssdata = +''
11303
12712
  next unless File.exist?(uri_path)
11304
12713
 
11305
12714
  open(uri_path) do |f|
@@ -11378,7 +12787,39 @@ protected
11378
12787
  html = html_a + html_b + html[(pos + 6)..-1]
11379
12788
  offset = (html_a + html_b).length
11380
12789
  end
12790
+
12791
+ offset = 0
12792
+ while (offset < html.length) && (pos = html.index('</textarea>', offset))
12793
+ html_a = html[0, offset]
12794
+ html_b = html[offset, pos - offset + 11]
12795
+ while html_b =~ %r@<textarea([^\>]*)>(.*?)\n(.*?)</textarea>@mi
12796
+ # preserve newlines on <textarea> tag
12797
+ html_b.gsub!(%r@<textarea([^\>]*)>(.*?)\n(.*?)</textarea>@mi, "<textarea\\1>\\2<TBR>\\3</textarea>")
12798
+ html_b.gsub!(%r@<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>@mi, "<textarea\\1>\\2''\\3</textarea>")
12799
+ end
12800
+ html = html_a + html_b + html[(pos + 11)..-1]
12801
+ offset = html_a.length + html_b.length
12802
+ end
12803
+ html.gsub!(/([\s]*)<option/mi, "<option")
12804
+ html.gsub!(%r@</option>([\s]*)@mi, "</option>")
12805
+ offset = 0
12806
+ while (offset < html.length) && (pos = html.index('</option>', offset))
12807
+ html_a = html[0, offset]
12808
+ html_b = html[offset, pos - offset + 9]
12809
+ while html_b =~ %r@<option([^\>]*)>(.*?)</option>@mi
12810
+ html_b.gsub!(%r@<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>@mi, "\\2\t\\4\r")
12811
+ html_b.gsub!(%r@<option([^\>]*)>(.*?)</option>@mi, "\\2\r")
12812
+ end
12813
+ html = html_a + html_b + html[(pos + 9)..-1]
12814
+ offset = html_a.length + html_b.length
12815
+ end
12816
+ html.gsub!(/<select([^\>]*)>/mi, "<select\\1 opt=\"")
12817
+ html.gsub!(%r@([\s]+)</select>@mi, "\" />")
11381
12818
  html.gsub!(/[\n]/, " ")
12819
+
12820
+ # restore textarea newlines
12821
+ html.gsub!('<TBR>', "\n")
12822
+
11382
12823
  # remove extra spaces from code
11383
12824
  html.gsub!(/[\s]+<\/(table|tr|td|th|ul|ol|li|dl|dt|dd)>/, '</\\1>')
11384
12825
  html.gsub!(/[\s]+<(tr|td|th|ul|ol|li|dl|dt|dd|br)/, '<\\1')
@@ -11389,6 +12830,8 @@ protected
11389
12830
  html.gsub!(/[\s]*<img/, ' <img')
11390
12831
  html.gsub!(/<img([^\>]*)>/xi, '<img\\1><span><marker style="font-size:0"/></span>')
11391
12832
  html.gsub!(/<xre/, '<pre') # restore pre tag
12833
+ html.gsub!(/<textarea([^\>]*)>/xi, '<textarea\\1 value="')
12834
+ html.gsub!(/<\/textarea>/, '" />')
11392
12835
 
11393
12836
  # trim string
11394
12837
  html.gsub!(/^[\s]+/, '')
@@ -11418,7 +12861,7 @@ protected
11418
12861
  dom[key]['fill'] = ((@textrendermode % 2) == 0)
11419
12862
  dom[key]['clip'] = (@textrendermode > 3)
11420
12863
  dom[key]['line-height'] = @cell_height_ratio
11421
- dom[key]['bgcolor'] = ActiveSupport::OrderedHash.new
12864
+ dom[key]['bgcolor'] = []
11422
12865
  dom[key]['fgcolor'] = @fgcolor.dup # color
11423
12866
  dom[key]['strokecolor'] = @strokecolor.dup
11424
12867
 
@@ -11483,7 +12926,7 @@ protected
11483
12926
  dom[grandparent]['cols'] = dom[(dom[key]['parent'])]['cols']
11484
12927
  end
11485
12928
  if (dom[key]['value'] == 'td') or (dom[key]['value'] == 'th')
11486
- dom[(dom[key]['parent'])]['content'] = ''
12929
+ dom[(dom[key]['parent'])]['content'] = +''
11487
12930
  (dom[key]['parent'] + 1).upto(key - 1) do |i|
11488
12931
  dom[(dom[key]['parent'])]['content'] << a[dom[i]['elkey']]
11489
12932
  end
@@ -11520,7 +12963,7 @@ protected
11520
12963
  # *** opening html tag
11521
12964
  dom[key]['opening'] = true
11522
12965
  dom[key]['parent'] = level[-1]
11523
- if element[-1, 1] == '/' or (dom[key]['value'] =~ /(br|img|hr)/)
12966
+ if element[-1, 1] == '/' or (dom[key]['value'] =~ /(br|img|hr|input)/)
11524
12967
  # self-closing tag
11525
12968
  dom[key]['self'] = true
11526
12969
  else
@@ -11546,7 +12989,7 @@ protected
11546
12989
  dom[key]['text-indent'] = dom[parentkey]['text-indent']
11547
12990
  end
11548
12991
  # get attributes
11549
- attr_array = element.scan(/([^=\s]*)[\s]*=[\s]*"([^"]*)"/)
12992
+ attr_array = element.scan(/([^=\s]+)[\s]*(?:=[\s]*"([^"]*)")*/)[1..-1]
11550
12993
  dom[key]['attribute'] = {} # reset attribute array
11551
12994
  attr_array.each do |name, value|
11552
12995
  dom[key]['attribute'][name.downcase] = value
@@ -11636,7 +13079,7 @@ protected
11636
13079
  end
11637
13080
  end
11638
13081
  # font style
11639
- dom[key]['fontstyle'] ||= ""
13082
+ dom[key]['fontstyle'] ||= +""
11640
13083
  if !dom[key]['style']['font-weight'].nil? and (dom[key]['style']['font-weight'][0,1].downcase == 'b')
11641
13084
  dom[key]['fontstyle'] << 'B'
11642
13085
  end
@@ -11645,13 +13088,13 @@ protected
11645
13088
  end
11646
13089
  # font color
11647
13090
  if !empty_string(dom[key]['style']['color'])
11648
- dom[key]['fgcolor'] = convertHTMLColorToDec(dom[key]['style']['color'])
13091
+ dom[key]['fgcolor'] = convert_html_color_to_dec_array(dom[key]['style']['color'])
11649
13092
  elsif dom[key]['value'] == 'a'
11650
13093
  dom[key]['fgcolor'] = @html_link_color_array
11651
13094
  end
11652
13095
  # background color
11653
13096
  if !empty_string(dom[key]['style']['background-color'])
11654
- dom[key]['bgcolor'] = convertHTMLColorToDec(dom[key]['style']['background-color'])
13097
+ dom[key]['bgcolor'] = convert_html_color_to_dec_array(dom[key]['style']['background-color'])
11655
13098
  end
11656
13099
  # text-decoration
11657
13100
  if !dom[key]['style']['text-decoration'].nil?
@@ -11814,17 +13257,17 @@ protected
11814
13257
  end
11815
13258
  # set foreground color attribute
11816
13259
  if !empty_string(dom[key]['attribute']['color'])
11817
- dom[key]['fgcolor'] = convertHTMLColorToDec(dom[key]['attribute']['color'])
13260
+ dom[key]['fgcolor'] = convert_html_color_to_dec_array(dom[key]['attribute']['color'])
11818
13261
  elsif (dom[key]['style'].nil? or dom[key]['style']['color'].nil?) and (dom[key]['value'] == 'a')
11819
13262
  dom[key]['fgcolor'] = @html_link_color_array
11820
13263
  end
11821
13264
  # set background color attribute
11822
13265
  if !empty_string(dom[key]['attribute']['bgcolor'])
11823
- dom[key]['bgcolor'] = convertHTMLColorToDec(dom[key]['attribute']['bgcolor'])
13266
+ dom[key]['bgcolor'] = convert_html_color_to_dec_array(dom[key]['attribute']['bgcolor'])
11824
13267
  end
11825
13268
  # set stroke color attribute
11826
13269
  if !empty_string(dom[key]['attribute']['strokecolor'])
11827
- dom[key]['strokecolor'] = convertHTMLColorToDec(dom[key]['attribute']['strokecolor'])
13270
+ dom[key]['strokecolor'] = convert_html_color_to_dec_array(dom[key]['attribute']['strokecolor'])
11828
13271
  end
11829
13272
  # check for width attribute
11830
13273
  if !dom[key]['attribute']['width'].nil?
@@ -11982,8 +13425,9 @@ public
11982
13425
  def sanitize_html(html)
11983
13426
  # Escape '<' character for not tag case.
11984
13427
  html = html.gsub(%r{(<+)([^/a-zA-Z])}){CGI.escapeHTML($1) + $2}.gsub(%r{</([^a-zA-Z])}){'&lt;/' + $1}
11985
-
11986
- html = "%s" % sanitize(html, :tags=> %w(a b blockquote body br dd del div dl dt em font h1 h2 h3 h4 h5 h6 hr i img li ol p pre small span strong sub sup table td th thead tr tt u ins ul), :attributes => %w(cellspacing cellpadding bgcolor color value width height src size colspan rowspan style align border face href name dir class id nobr stroke strokecolor fill nested tablehead))
13428
+ "%s" % sanitize(html,
13429
+ :tags=> %w(a b blockquote body br dd del div dl dt em font form h1 h2 h3 h4 h5 h6 hr i img input label li ol option p pre select small span strong sub sup table td textarea th thead tr tt u ins ul),
13430
+ :attributes => %w(cellspacing cellpadding bgcolor color value width height src size colspan rowspan style align border face href name dir class id nobr stroke strokecolor fill nested tablehead cols rows type action enctype method maxlength onclick multiple checked disabled))
11987
13431
  end
11988
13432
  protected :sanitize_html
11989
13433
 
@@ -12083,6 +13527,7 @@ public
12083
13527
  prev_listordered = @listordered
12084
13528
  prev_listcount = @listcount
12085
13529
  prev_lispacer = @lispacer
13530
+ prev_li_position_x = @li_position_x
12086
13531
  @listnum = 0
12087
13532
  @listordered = []
12088
13533
  @listcount = []
@@ -12172,8 +13617,8 @@ public
12172
13617
  # restore previous object
12173
13618
  rollbackTransaction(true)
12174
13619
  # restore previous values
12175
- this_method_vars.each {|vkey , vval|
12176
- eval("#{vkey} = vval")
13620
+ this_method_vars.each {|vkey, vval|
13621
+ binding.local_variable_set(vkey, vval)
12177
13622
  }
12178
13623
  # add a page (or trig AcceptPageBreak() for multicolumn mode)
12179
13624
  pre_y = @y
@@ -12946,7 +14391,7 @@ public
12946
14391
  len1 = dom[key]['value'].length
12947
14392
  lsp = len1 - dom[key]['value'].lstrip.length
12948
14393
  rsp = len1 - dom[key]['value'].rstrip.length
12949
- tmpstr = ''
14394
+ tmpstr = +''
12950
14395
  if rsp > 0
12951
14396
  tmpstr << dom[key]['value'][-rsp..-1]
12952
14397
  end
@@ -13036,7 +14481,7 @@ public
13036
14481
  rollbackTransaction(true)
13037
14482
  # restore previous values
13038
14483
  this_method_vars.each {|vkey , vval|
13039
- eval("#{vkey} = vval")
14484
+ binding.local_variable_set(vkey, vval)
13040
14485
  }
13041
14486
  # add a page (or trig AcceptPageBreak() for multicolumn mode)
13042
14487
  pre_y = @y
@@ -13155,6 +14600,7 @@ public
13155
14600
  @listordered = prev_listordered
13156
14601
  @listcount = prev_listcount
13157
14602
  @lispacer = prev_lispacer
14603
+ @li_position_x = prev_li_position_x
13158
14604
  dom = nil
13159
14605
  rescue => err
13160
14606
  Error('writeHTML Error.', err)
@@ -13471,6 +14917,7 @@ public
13471
14917
  @lispacer = '!'
13472
14918
  end
13473
14919
  end
14920
+ @li_position_x = @x
13474
14921
  when 'blockquote'
13475
14922
  if @rtl
13476
14923
  @r_margin += @listindent
@@ -13494,6 +14941,166 @@ public
13494
14941
  SetXY(GetX(), GetY() + ((0.3 * @font_size_pt) / @k))
13495
14942
  when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
13496
14943
  addHTMLVertSpace(hbz, hb, cell, firstorlast)
14944
+
14945
+ # Form fields (since 4.8.000 - 2009-09-07)
14946
+ when 'form'
14947
+ if tag['attribute']['action']
14948
+ @form_action = tag['attribute']['action']
14949
+ else
14950
+ @form_action = @@k_path_url + request&.base_url.to_s
14951
+ end
14952
+ if tag['attribute']['enctype']
14953
+ @form_enctype = tag['attribute']['enctype']
14954
+ else
14955
+ @form_enctype = 'application/x-www-form-urlencoded'
14956
+ end
14957
+ if tag['attribute']['method']
14958
+ @form_mode = tag['attribute']['method']
14959
+ else
14960
+ @form_mode = 'post'
14961
+ end
14962
+ when 'input'
14963
+ if tag['attribute']['type'] == 'checkbox' || (tag['attribute']['name'] && !empty_string(tag['attribute']['name']))
14964
+ name = tag['attribute']['name'] || rand.to_s
14965
+ prop = {}
14966
+ opt = {}
14967
+ if tag['attribute']['value'] && !empty_string(tag['attribute']['value'])
14968
+ value = tag['attribute']['value']
14969
+ end
14970
+ if tag['attribute']['maxlength'] && !empty_string(tag['attribute']['maxlength'])
14971
+ opt['maxlen'] = tag['attribute']['maxlength'].to_i
14972
+ end
14973
+ h = @font_size * @cell_height_ratio
14974
+ if tag['attribute']['size'] && !empty_string(tag['attribute']['size'])
14975
+ w = tag['attribute']['size'].to_i * GetStringWidth(32.chr) * 2
14976
+ else
14977
+ w = h
14978
+ end
14979
+ if tag['attribute'].key? 'disabled'
14980
+ prop['readonly'] = 'true'
14981
+ end
14982
+ if tag['attribute'].key? 'checked'
14983
+ checked = true
14984
+ else
14985
+ checked = false
14986
+ end
14987
+
14988
+ case tag['attribute']['type']
14989
+ when 'text'
14990
+ opt['v'] = value if value
14991
+ TextField(name, w, h, prop, opt, '', '', false)
14992
+ when 'password'
14993
+ opt['v'] = value if value
14994
+ prop['password'] = 'true'
14995
+ TextField(name, w, h, prop, opt, '', '', false)
14996
+ when 'checkbox'
14997
+ CheckBox(name, w, checked, prop, opt, value, '', '', false)
14998
+ when 'radio'
14999
+ RadioButton(name, w, prop, opt, value, checked, '', '', false)
15000
+ when 'submit'
15001
+ w = GetStringWidth(value) * 1.5
15002
+ h *= 1.6
15003
+ prop = {'lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>[196, 196, 196], 'strokeColor'=>[255, 255, 255]}
15004
+ action = {}
15005
+ action['S'] = 'SubmitForm'
15006
+ action['F'] = @form_action
15007
+ action['Flags'] = ['ExportFormat'] unless @form_enctype == 'FDF'
15008
+ action['Flags'] = ['GetMethod'] if @form_mode == 'get'
15009
+ Button(name, w, h, value, action, prop, opt, '', '', false)
15010
+ when 'reset'
15011
+ w = GetStringWidth(value) * 1.5
15012
+ h *= 1.6
15013
+ prop = {'lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>[196, 196, 196], 'strokeColor'=>[255, 255, 255]}
15014
+ Button(name, w, h, value, {'S'=>'ResetForm'}, prop, opt, '', '', false)
15015
+ when 'file'
15016
+ prop['fileSelect'] = 'true'
15017
+ TextField(name, w, h, prop, opt, '', '', false)
15018
+ value ||= '*'
15019
+ w = GetStringWidth(value) * 2
15020
+ h *= 1.2
15021
+ prop = {'lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>[196, 196, 196], 'strokeColor'=>[255, 255, 255]}
15022
+ jsaction = "var f=this.getField('#{name}'); f.browseForFileToSubmit();"
15023
+ Button('FB_' + name, w, h, value, jsaction, prop, opt, '', '', false)
15024
+ when 'hidden'
15025
+ opt['v'] = value if value
15026
+ opt['f'] = ['invisible', 'hidden']
15027
+ TextField(name, 0, 0, prop, opt, '', '', false)
15028
+ when 'image'
15029
+ # THIS TYPE MUST BE FIXED
15030
+ if tag['attribute']['src'] && !empty_string(tag['attribute']['src'])
15031
+ img = tag['attribute']['src']
15032
+ value = 'img'
15033
+ #opt['mk'] = {'i'=>img, 'tp'=>1, 'if'=>{'sw'=>'A', 's'=>'A', 'fb'=>false}}
15034
+ if tag['attribute']['onclick'] && !tag['attribute']['onclick'].empty?
15035
+ jsaction = tag['attribute']['onclick']
15036
+ else
15037
+ jsaction = ''
15038
+ end
15039
+ Button(name, w, h, value, jsaction, prop, opt, '', '', false)
15040
+ end
15041
+ when 'button'
15042
+ w = GetStringWidth(value) * 1.5
15043
+ h *= 1.6
15044
+ prop = {'lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>[196, 196, 196], 'strokeColor'=>[255, 255, 255]}
15045
+ if tag['attribute']['onclick'] && !tag['attribute']['onclick'].empty?
15046
+ jsaction = tag['attribute']['onclick']
15047
+ else
15048
+ jsaction = ''
15049
+ end
15050
+ Button(name, w, h, value, jsaction, prop, opt, '', '', false)
15051
+ end
15052
+ end
15053
+ when 'textarea'
15054
+ if tag['attribute']['name'] && !empty_string(tag['attribute']['name'])
15055
+ prop = {}
15056
+ opt = {}
15057
+ name = tag['attribute']['name']
15058
+ if tag['attribute']['value'] && !empty_string(tag['attribute']['value'])
15059
+ opt['v'] = tag['attribute']['value']
15060
+ end
15061
+ if tag['attribute']['cols'] && !empty_string(tag['attribute']['cols'])
15062
+ w = tag['attribute']['cols'].to_i * GetStringWidth(32.chr) * 2
15063
+ else
15064
+ w = 40
15065
+ end
15066
+ if tag['attribute']['rows'] && !empty_string(tag['attribute']['rows'])
15067
+ h = tag['attribute']['rows'].to_i * @font_size * @cell_height_ratio
15068
+ else
15069
+ h = 10
15070
+ end
15071
+ prop['multiline'] = 'true'
15072
+ TextField(name, w, h, prop, opt, '', '', false)
15073
+ end
15074
+ when 'select'
15075
+ if tag['attribute']['name'] && !empty_string(tag['attribute']['name']) && tag['attribute']['opt'] && !empty_string(tag['attribute']['opt'])
15076
+ h = @font_size * @cell_height_ratio
15077
+ if tag['attribute']['size'] && !empty_string(tag['attribute']['size'])
15078
+ h *= (tag['attribute']['size'].to_i + 1)
15079
+ end
15080
+ prop = {}
15081
+ opt = {}
15082
+ name = tag['attribute']['name']
15083
+ w = 0
15084
+ options = tag['attribute']['opt'].split("\r")
15085
+ values = []
15086
+ options.each {|val|
15087
+ if val.index("\t")
15088
+ opts = val.split("\t")
15089
+ values << opts
15090
+ w = [w, GetStringWidth(opts[1])].max
15091
+ else
15092
+ values << val
15093
+ w = [w, GetStringWidth(val)].max
15094
+ end
15095
+ }
15096
+ w *= 2
15097
+ if tag['attribute'].key? 'multiple'
15098
+ prop['multipleSelection'] = 'true'
15099
+ ListBox(name, w, h, values, prop, opt, '', '', false)
15100
+ else
15101
+ ComboBox(name, w, h, values, prop, opt, '', '', false)
15102
+ end
15103
+ end
13497
15104
  end
13498
15105
 
13499
15106
  if dom[key]['self'] and dom[key]['attribute']['pagebreakafter']
@@ -13569,317 +15176,325 @@ public
13569
15176
  end
13570
15177
  # Closing tag
13571
15178
  case (tag['value'])
13572
- when 'tr'
13573
- table_el = dom[(dom[key]['parent'])]['parent']
13574
- if parent['endy'].nil?
13575
- dom[(dom[key]['parent'])]['endy'] = @y
13576
- parent['endy'] = @y
15179
+ when 'tr'
15180
+ table_el = dom[(dom[key]['parent'])]['parent']
15181
+ if parent['endy'].nil?
15182
+ dom[(dom[key]['parent'])]['endy'] = @y
15183
+ parent['endy'] = @y
15184
+ end
15185
+ if parent['endpage'].nil?
15186
+ dom[(dom[key]['parent'])]['endpage'] = @page
15187
+ parent['endpage'] = @page
15188
+ end
15189
+ # update row-spanned cells
15190
+ if !dom[table_el]['rowspans'].nil?
15191
+ dom[table_el]['rowspans'].each_with_index { |trwsp, k|
15192
+ dom[table_el]['rowspans'][k]['rowspan'] -= 1
15193
+ if dom[table_el]['rowspans'][k]['rowspan'] == 0
15194
+ if dom[table_el]['rowspans'][k]['endpage'] == parent['endpage']
15195
+ dom[(dom[key]['parent'])]['endy'] = [dom[table_el]['rowspans'][k]['endy'], parent['endy']].max
15196
+ elsif dom[table_el]['rowspans'][k]['endpage'] > parent['endpage']
15197
+ dom[(dom[key]['parent'])]['endy'] = dom[table_el]['rowspans'][k]['endy']
15198
+ dom[(dom[key]['parent'])]['endpage'] = dom[table_el]['rowspans'][k]['endpage']
15199
+ end
15200
+ end
15201
+ }
15202
+ # report new endy and endpage to the rowspanned cells
15203
+ dom[table_el]['rowspans'].each_with_index { |trwsp, k|
15204
+ if dom[table_el]['rowspans'][k]['rowspan'] == 0
15205
+ dom[table_el]['rowspans'][k]['endpage'] = [dom[table_el]['rowspans'][k]['endpage'], dom[(dom[key]['parent'])]['endpage']].max
15206
+ dom[(dom[key]['parent'])]['endpage'] = dom[table_el]['rowspans'][k]['endpage']
15207
+ dom[table_el]['rowspans'][k]['endy'] = [dom[table_el]['rowspans'][k]['endy'], dom[(dom[key]['parent'])]['endy']].max
15208
+ dom[(dom[key]['parent'])]['endy'] = dom[table_el]['rowspans'][k]['endy']
15209
+ end
15210
+ }
15211
+ # update remaining rowspanned cells
15212
+ dom[table_el]['rowspans'].each_with_index { |trwsp, k|
15213
+ if dom[table_el]['rowspans'][k]['rowspan'] == 0
15214
+ dom[table_el]['rowspans'][k]['endpage'] = dom[(dom[key]['parent'])]['endpage']
15215
+ dom[table_el]['rowspans'][k]['endy'] = dom[(dom[key]['parent'])]['endy']
15216
+ end
15217
+ }
15218
+ end
15219
+ if (@num_columns > 1) and (dom[(dom[key]['parent'])]['endy'] >= (@page_break_trigger - @lasth)) and (@y < dom[(dom[key]['parent'])]['endy'])
15220
+ Ln(0, cell)
15221
+ else
15222
+ setPage(dom[(dom[key]['parent'])]['endpage']);
15223
+ @y = dom[(dom[key]['parent'])]['endy']
15224
+ if !dom[table_el]['attribute']['cellspacing'].nil?
15225
+ cellspacing = getHTMLUnitToUnits(dom[table_el]['attribute']['cellspacing'], 1, 'px')
15226
+ @y += cellspacing
13577
15227
  end
13578
- if parent['endpage'].nil?
13579
- dom[(dom[key]['parent'])]['endpage'] = @page
13580
- parent['endpage'] = @page
15228
+ Ln(0, cell)
15229
+ @x = parent['startx']
15230
+ # account for booklet mode
15231
+ if parent['startpage'] and @page > parent['startpage']
15232
+ if @rtl and (@pagedim[@page]['orm'] != @pagedim[parent['startpage']]['orm'])
15233
+ @x -= @pagedim[@page]['orm'] - @pagedim[parent['startpage']]['orm']
15234
+ elsif !@rtl and (@pagedim[@page]['olm'] != @pagedim[parent['startpage']]['olm'])
15235
+ @x += @pagedim[@page]['olm'] - @pagedim[parent['startpage']]['olm']
15236
+ end
13581
15237
  end
15238
+ end
15239
+ when 'table'
15240
+ if dom[(dom[key]['parent'])]['attribute']['tablehead'] and dom[(dom[key]['parent'])]['attribute']['tablehead'] == "1"
15241
+ # closing tag used for the thead part
15242
+ in_table_head = true
15243
+ @in_thead = false
15244
+ end
15245
+
15246
+ table_el = parent
15247
+ # draw borders
15248
+ if (!table_el['attribute']['border'].nil? and (table_el['attribute']['border'].to_i > 0)) or (!table_el['style'].nil? and !table_el['style']['border'].nil? and (table_el['style']['border'].to_i > 0))
15249
+ border = 1
15250
+ else
15251
+ border = 0
15252
+ end
15253
+
15254
+ startpage = 0
15255
+ end_page = 0
15256
+ # fix bottom line alignment of last line before page break
15257
+ dom[(dom[key]['parent'])]['trids'].each_with_index { |trkey, j|
13582
15258
  # update row-spanned cells
13583
- if !dom[table_el]['rowspans'].nil?
13584
- dom[table_el]['rowspans'].each_with_index { |trwsp, k|
13585
- dom[table_el]['rowspans'][k]['rowspan'] -= 1
13586
- if dom[table_el]['rowspans'][k]['rowspan'] == 0
13587
- if dom[table_el]['rowspans'][k]['endpage'] == parent['endpage']
13588
- dom[(dom[key]['parent'])]['endy'] = [dom[table_el]['rowspans'][k]['endy'], parent['endy']].max
13589
- elsif dom[table_el]['rowspans'][k]['endpage'] > parent['endpage']
13590
- dom[(dom[key]['parent'])]['endy'] = dom[table_el]['rowspans'][k]['endy']
13591
- dom[(dom[key]['parent'])]['endpage'] = dom[table_el]['rowspans'][k]['endpage']
13592
- end
13593
- end
13594
- }
13595
- # report new endy and endpage to the rowspanned cells
13596
- dom[table_el]['rowspans'].each_with_index { |trwsp, k|
13597
- if dom[table_el]['rowspans'][k]['rowspan'] == 0
13598
- dom[table_el]['rowspans'][k]['endpage'] = [dom[table_el]['rowspans'][k]['endpage'], dom[(dom[key]['parent'])]['endpage']].max
13599
- dom[(dom[key]['parent'])]['endpage'] = dom[table_el]['rowspans'][k]['endpage']
13600
- dom[table_el]['rowspans'][k]['endy'] = [dom[table_el]['rowspans'][k]['endy'], dom[(dom[key]['parent'])]['endy']].max
13601
- dom[(dom[key]['parent'])]['endy'] = dom[table_el]['rowspans'][k]['endy']
15259
+ if !dom[(dom[key]['parent'])]['rowspans'].nil?
15260
+ dom[(dom[key]['parent'])]['rowspans'].each_with_index { |trwsp, k|
15261
+ if trwsp['trid'] == trkey
15262
+ dom[(dom[key]['parent'])]['rowspans'][k]['mrowspan'] -= 1
13602
15263
  end
13603
- }
13604
- # update remaining rowspanned cells
13605
- dom[table_el]['rowspans'].each_with_index { |trwsp, k|
13606
- if dom[table_el]['rowspans'][k]['rowspan'] == 0
13607
- dom[table_el]['rowspans'][k]['endpage'] = dom[(dom[key]['parent'])]['endpage']
13608
- dom[table_el]['rowspans'][k]['endy'] = dom[(dom[key]['parent'])]['endy']
15264
+ if defined?(prevtrkey) and (trwsp['trid'] == prevtrkey) and (trwsp['mrowspan'] >= 0)
15265
+ dom[(dom[key]['parent'])]['rowspans'][k]['trid'] = trkey
13609
15266
  end
13610
15267
  }
13611
15268
  end
13612
- if (@num_columns > 1) and (dom[(dom[key]['parent'])]['endy'] >= (@page_break_trigger - @lasth)) and (@y < dom[(dom[key]['parent'])]['endy'])
13613
- Ln(0, cell)
13614
- else
13615
- setPage(dom[(dom[key]['parent'])]['endpage']);
13616
- @y = dom[(dom[key]['parent'])]['endy']
13617
- if !dom[table_el]['attribute']['cellspacing'].nil?
13618
- cellspacing = getHTMLUnitToUnits(dom[table_el]['attribute']['cellspacing'], 1, 'px')
13619
- @y += cellspacing
13620
- end
13621
- Ln(0, cell)
13622
- @x = parent['startx']
13623
- # account for booklet mode
13624
- if parent['startpage'] and @page > parent['startpage']
13625
- if @rtl and (@pagedim[@page]['orm'] != @pagedim[parent['startpage']]['orm'])
13626
- @x -= @pagedim[@page]['orm'] - @pagedim[parent['startpage']]['orm']
13627
- elsif !@rtl and (@pagedim[@page]['olm'] != @pagedim[parent['startpage']]['olm'])
13628
- @x += @pagedim[@page]['olm'] - @pagedim[parent['startpage']]['olm']
13629
- end
13630
- end
13631
- end
13632
- when 'table'
13633
- if dom[(dom[key]['parent'])]['attribute']['tablehead'] and dom[(dom[key]['parent'])]['attribute']['tablehead'] == "1"
13634
- # closing tag used for the thead part
13635
- in_table_head = true
13636
- @in_thead = false
13637
- end
13638
-
13639
- table_el = parent
13640
- # draw borders
13641
- if (!table_el['attribute']['border'].nil? and (table_el['attribute']['border'].to_i > 0)) or (!table_el['style'].nil? and !table_el['style']['border'].nil? and (table_el['style']['border'].to_i > 0))
13642
- border = 1
13643
- else
13644
- border = 0
13645
- end
13646
-
13647
- startpage = 0
13648
- end_page = 0
13649
- # fix bottom line alignment of last line before page break
13650
- dom[(dom[key]['parent'])]['trids'].each_with_index { |trkey, j|
15269
+ if defined?(prevtrkey) and (dom[trkey]['startpage'] > dom[prevtrkey]['endpage'])
15270
+ pgendy = @pagedim[dom[prevtrkey]['endpage']]['hk'] - @pagedim[dom[prevtrkey]['endpage']]['bm']
15271
+ dom[prevtrkey]['endy'] = pgendy
13651
15272
  # update row-spanned cells
13652
15273
  if !dom[(dom[key]['parent'])]['rowspans'].nil?
13653
15274
  dom[(dom[key]['parent'])]['rowspans'].each_with_index { |trwsp, k|
13654
- if trwsp['trid'] == trkey
13655
- dom[(dom[key]['parent'])]['rowspans'][k]['mrowspan'] -= 1
13656
- end
13657
- if defined?(prevtrkey) and (trwsp['trid'] == prevtrkey) and (trwsp['mrowspan'] >= 0)
13658
- dom[(dom[key]['parent'])]['rowspans'][k]['trid'] = trkey
15275
+ if (trwsp['trid'] == trkey) and (trwsp['mrowspan'] > 1) and (trwsp['endpage'] == dom[prevtrkey]['endpage'])
15276
+ dom[(dom[key]['parent'])]['rowspans'][k]['endy'] = pgendy
15277
+ dom[(dom[key]['parent'])]['rowspans'][k]['mrowspan'] = -1
13659
15278
  end
13660
15279
  }
13661
15280
  end
13662
- if defined?(prevtrkey) and (dom[trkey]['startpage'] > dom[prevtrkey]['endpage'])
13663
- pgendy = @pagedim[dom[prevtrkey]['endpage']]['hk'] - @pagedim[dom[prevtrkey]['endpage']]['bm']
13664
- dom[prevtrkey]['endy'] = pgendy
13665
- # update row-spanned cells
13666
- if !dom[(dom[key]['parent'])]['rowspans'].nil?
13667
- dom[(dom[key]['parent'])]['rowspans'].each_with_index { |trwsp, k|
13668
- if (trwsp['trid'] == trkey) and (trwsp['mrowspan'] > 1) and (trwsp['endpage'] == dom[prevtrkey]['endpage'])
13669
- dom[(dom[key]['parent'])]['rowspans'][k]['endy'] = pgendy
13670
- dom[(dom[key]['parent'])]['rowspans'][k]['mrowspan'] = -1
13671
- end
13672
- }
13673
- end
15281
+ end
15282
+ prevtrkey = trkey
15283
+ table_el = dom[(dom[key]['parent'])].dup
15284
+ }
15285
+ # for each row
15286
+ table_el['trids'].each_with_index { |trkey, j|
15287
+ parent = dom[trkey]
15288
+ # for each cell on the row
15289
+ parent['cellpos'].each_with_index { |cellpos, k|
15290
+ if !cellpos['rowspanid'].nil? and (cellpos['rowspanid'] >= 0)
15291
+ cellpos['startx'] = table_el['rowspans'][(cellpos['rowspanid'])]['startx']
15292
+ cellpos['endx'] = table_el['rowspans'][(cellpos['rowspanid'])]['endx']
15293
+ endy = table_el['rowspans'][(cellpos['rowspanid'])]['endy']
15294
+ startpage = table_el['rowspans'][(cellpos['rowspanid'])]['startpage']
15295
+ end_page = table_el['rowspans'][(cellpos['rowspanid'])]['endpage']
15296
+ else
15297
+ endy = parent['endy']
15298
+ startpage = parent['startpage']
15299
+ end_page = parent['endpage']
13674
15300
  end
13675
- prevtrkey = trkey
13676
- table_el = dom[(dom[key]['parent'])].dup
13677
- }
13678
- # for each row
13679
- table_el['trids'].each_with_index { |trkey, j|
13680
- parent = dom[trkey]
13681
- # for each cell on the row
13682
- parent['cellpos'].each_with_index { |cellpos, k|
13683
- if !cellpos['rowspanid'].nil? and (cellpos['rowspanid'] >= 0)
13684
- cellpos['startx'] = table_el['rowspans'][(cellpos['rowspanid'])]['startx']
13685
- cellpos['endx'] = table_el['rowspans'][(cellpos['rowspanid'])]['endx']
13686
- endy = table_el['rowspans'][(cellpos['rowspanid'])]['endy']
13687
- startpage = table_el['rowspans'][(cellpos['rowspanid'])]['startpage']
13688
- end_page = table_el['rowspans'][(cellpos['rowspanid'])]['endpage']
13689
- else
13690
- endy = parent['endy']
13691
- startpage = parent['startpage']
13692
- end_page = parent['endpage']
13693
- end
13694
- cellpos['startx'] ||= 0
13695
- if end_page > startpage
13696
- # design borders around HTML cells.
13697
- startpage.upto(end_page) do |page|
13698
- setPage(page)
13699
- if page == startpage
13700
- @y = parent['starty'] # put cursor at the beginning of row on the first page
13701
- ch = getPageHeight() - parent['starty'] - getBreakMargin()
13702
- cborder = getBorderMode(border, position='start')
13703
- elsif page == end_page
13704
- @y = @t_margin # put cursor at the beginning of last page
13705
- ch = endy - @t_margin
13706
- cborder = getBorderMode(border, position='end')
13707
- else
13708
- @y = @t_margin # put cursor at the beginning of the current page
13709
- ch = getPageHeight() - @t_margin - getBreakMargin()
13710
- cborder = getBorderMode(border, position='middle')
13711
- end
13712
- if !cellpos['bgcolor'].nil? and (cellpos['bgcolor'] != false)
13713
- SetFillColorArray(cellpos['bgcolor'])
13714
- fill = 1
13715
- else
13716
- fill = 0
13717
- end
13718
- cw = (cellpos['endx'] - cellpos['startx']).abs
13719
- @x = cellpos['startx']
13720
- # account for margin changes
13721
- if page > startpage
13722
- if @rtl and (@pagedim[page]['orm'] != @pagedim[startpage]['orm'])
13723
- @x -= @pagedim[page]['orm'] - @pagedim[startpage]['orm']
13724
- elsif !@rtl and (@pagedim[page]['lm'] != @pagedim[startpage]['olm'])
13725
- @x += @pagedim[page]['olm'] - @pagedim[startpage]['olm']
13726
- end
13727
- end
13728
-
13729
- prevLastH = @lasth
13730
- # design a cell around the text
13731
- ccode = @fill_color + "\n" + getCellCode(cw, ch, '', cborder, 1, '', fill, '', 0, true)
13732
- @lasth = prevLastH
13733
-
13734
- if (cborder != 0) or (fill == 1)
13735
- pagebuff = getPageBuffer(@page)
13736
- pstart = pagebuff[0, @intmrk[@page]]
13737
- pend = pagebuff[@intmrk[@page]..-1]
13738
- setPageBuffer(@page, pstart + ccode + "\n" + pend)
13739
- @intmrk[@page] += (ccode + "\n").length
13740
- end
15301
+ cellpos['startx'] ||= 0
15302
+ if end_page > startpage
15303
+ # design borders around HTML cells.
15304
+ startpage.upto(end_page) do |page|
15305
+ setPage(page)
15306
+ if page == startpage
15307
+ @y = parent['starty'] # put cursor at the beginning of row on the first page
15308
+ ch = getPageHeight() - parent['starty'] - getBreakMargin()
15309
+ cborder = getBorderMode(border, position='start')
15310
+ elsif page == end_page
15311
+ @y = @t_margin # put cursor at the beginning of last page
15312
+ ch = endy - @t_margin
15313
+ cborder = getBorderMode(border, position='end')
15314
+ else
15315
+ @y = @t_margin # put cursor at the beginning of the current page
15316
+ ch = getPageHeight() - @t_margin - getBreakMargin()
15317
+ cborder = getBorderMode(border, position='middle')
13741
15318
  end
13742
- else
13743
- setPage(startpage)
13744
15319
  if !cellpos['bgcolor'].nil? and (cellpos['bgcolor'] != false)
13745
15320
  SetFillColorArray(cellpos['bgcolor'])
13746
15321
  fill = 1
13747
15322
  else
13748
15323
  fill = 0
13749
15324
  end
13750
- @x = cellpos['startx']
13751
- @y = parent['starty']
13752
15325
  cw = (cellpos['endx'] - cellpos['startx']).abs
13753
- ch = endy - parent['starty']
15326
+ @x = cellpos['startx']
15327
+ # account for margin changes
15328
+ if page > startpage
15329
+ if @rtl and (@pagedim[page]['orm'] != @pagedim[startpage]['orm'])
15330
+ @x -= @pagedim[page]['orm'] - @pagedim[startpage]['orm']
15331
+ elsif !@rtl and (@pagedim[page]['lm'] != @pagedim[startpage]['olm'])
15332
+ @x += @pagedim[page]['olm'] - @pagedim[startpage]['olm']
15333
+ end
15334
+ end
13754
15335
 
13755
15336
  prevLastH = @lasth
13756
15337
  # design a cell around the text
13757
- ccode = @fill_color + "\n" + getCellCode(cw, ch, '', border, 1, '', fill, '', 0, true)
15338
+ ccode = @fill_color + "\n" + getCellCode(cw, ch, '', cborder, 1, '', fill, '', 0, true)
13758
15339
  @lasth = prevLastH
13759
15340
 
13760
- if (border != 0) or (fill == 1)
13761
- if !@transfmrk[@page].nil?
13762
- pagemark = @transfmrk[@page]
13763
- @transfmrk[@page] += (ccode + "\n").length
13764
- elsif @in_footer
13765
- pagemark = @footerpos[@page]
13766
- @footerpos[@page] += (ccode + "\n").length
13767
- else
13768
- pagemark = @intmrk[@page]
13769
- @intmrk[@page] += (ccode + "\n").length
13770
- end
15341
+ if (cborder != 0) or (fill == 1)
13771
15342
  pagebuff = getPageBuffer(@page)
13772
- pstart = pagebuff[0, pagemark]
13773
- pend = pagebuff[pagemark..-1]
15343
+ pstart = pagebuff[0, @intmrk[@page]]
15344
+ pend = pagebuff[@intmrk[@page]..-1]
13774
15345
  setPageBuffer(@page, pstart + ccode + "\n" + pend)
15346
+ @intmrk[@page] += (ccode + "\n").length
13775
15347
  end
13776
15348
  end
13777
- }
13778
- if !table_el['attribute']['cellspacing'].nil?
13779
- cellspacing = getHTMLUnitToUnits(table_el['attribute']['cellspacing'], 1, 'px')
13780
- @y += cellspacing
13781
- end
13782
- Ln(0, cell)
13783
- @x = parent['startx']
13784
- if end_page > startpage
13785
- if @rtl and (@pagedim[end_page]['orm'] != @pagedim[startpage]['orm'])
13786
- @x += @pagedim[end_page]['orm'] - @pagedim[startpage]['orm']
13787
- elsif !@rtl and (@pagedim[end_page]['olm'] != @pagedim[startpage]['olm'])
13788
- @x += @pagedim[end_page]['olm'] - @pagedim[startpage]['olm']
15349
+ else
15350
+ setPage(startpage)
15351
+ if !cellpos['bgcolor'].nil? and (cellpos['bgcolor'] != false)
15352
+ SetFillColorArray(cellpos['bgcolor'])
15353
+ fill = 1
15354
+ else
15355
+ fill = 0
15356
+ end
15357
+ @x = cellpos['startx']
15358
+ @y = parent['starty']
15359
+ cw = (cellpos['endx'] - cellpos['startx']).abs
15360
+ ch = endy - parent['starty']
15361
+
15362
+ prevLastH = @lasth
15363
+ # design a cell around the text
15364
+ ccode = @fill_color + "\n" + getCellCode(cw, ch, '', border, 1, '', fill, '', 0, true)
15365
+ @lasth = prevLastH
15366
+
15367
+ if (border != 0) or (fill == 1)
15368
+ if !@transfmrk[@page].nil?
15369
+ pagemark = @transfmrk[@page]
15370
+ @transfmrk[@page] += (ccode + "\n").length
15371
+ elsif @in_footer
15372
+ pagemark = @footerpos[@page]
15373
+ @footerpos[@page] += (ccode + "\n").length
15374
+ else
15375
+ pagemark = @intmrk[@page]
15376
+ @intmrk[@page] += (ccode + "\n").length
15377
+ end
15378
+ pagebuff = getPageBuffer(@page)
15379
+ pstart = pagebuff[0, pagemark]
15380
+ pend = pagebuff[pagemark..-1]
15381
+ setPageBuffer(@page, pstart + ccode + "\n" + pend)
13789
15382
  end
13790
15383
  end
13791
15384
  }
13792
- if !in_table_head
13793
- # we are not inside a thead section
13794
- if dom[(parent['parent'])]['attribute']['cellpadding'] ### fix ###
13795
- @c_margin = @old_c_margin
15385
+ if !table_el['attribute']['cellspacing'].nil?
15386
+ cellspacing = getHTMLUnitToUnits(table_el['attribute']['cellspacing'], 1, 'px')
15387
+ @y += cellspacing
15388
+ end
15389
+ Ln(0, cell)
15390
+ @x = parent['startx']
15391
+ if end_page > startpage
15392
+ if @rtl and (@pagedim[end_page]['orm'] != @pagedim[startpage]['orm'])
15393
+ @x += @pagedim[end_page]['orm'] - @pagedim[startpage]['orm']
15394
+ elsif !@rtl and (@pagedim[end_page]['olm'] != @pagedim[startpage]['olm'])
15395
+ @x += @pagedim[end_page]['olm'] - @pagedim[startpage]['olm']
13796
15396
  end
13797
- @lasth = @font_size * @cell_height_ratio
13798
- if (@page == @numpages - 1) and @pageopen[@numpages]
13799
- # remove last blank page
13800
- deletePage(@numpages)
13801
- end
13802
- if !@thead_margins['top'].nil?
13803
- # restore top margin
13804
- @t_margin = @thead_margins['top']
13805
- @pagedim[@page]['tm'] = @t_margin
13806
- end
13807
- if table_el['attribute']['nested'].nil? or (table_el['attribute']['nested'] != 'true')
13808
- # reset main table header
13809
- @thead = ''
13810
- @thead_margins = {}
13811
- end
13812
- end
13813
- if tag['block']
13814
- unless dom[(dom[key]['parent'])]['attribute']['tablehead'] and dom[(dom[key]['parent'])]['attribute']['tablehead'] == "1" ### fix ###
13815
- addHTMLVertSpace(hbz / 2, 0, cell, (dom[key+1].nil? or (dom[key+1]['value'] != 'table'))) ### fix ###
13816
- end
13817
- end
13818
- when 'a'
13819
- @href = {}
13820
- @html_anchor = nil
13821
- when 'sup'
13822
- SetXY(GetX(), GetY() + (0.7 * parent['fontsize'] / @k))
13823
- when 'sub'
13824
- SetXY(GetX(), GetY() - (0.3 * parent['fontsize'] / @k))
13825
- when 'div'
13826
- addHTMLVertSpace(hbz, 0, cell, firstorlast)
13827
- when 'blockquote'
13828
- if @rtl
13829
- @r_margin -= @listindent
13830
- else
13831
- @l_margin -= @listindent
13832
15397
  end
13833
- @listindentlevel -= 1
13834
- addHTMLVertSpace(hbz, hb, cell, firstorlast)
13835
- when 'p'
13836
- addHTMLVertSpace(hbz, hb, cell, firstorlast)
13837
- when 'pre'
13838
- addHTMLVertSpace(hbz, hb, cell, firstorlast)
13839
- @premode = false
13840
- when 'dl'
13841
- @listnum -= 1
13842
- if @listnum <= 0
13843
- @listnum = 0
13844
- addHTMLVertSpace(hbz, hb, cell, firstorlast)
13845
- else
13846
- addHTMLVertSpace(0, 0, cell, firstorlast)
15398
+ }
15399
+ if !in_table_head
15400
+ # we are not inside a thead section
15401
+ if dom[(parent['parent'])]['attribute']['cellpadding'] ### fix ###
15402
+ @c_margin = @old_c_margin
13847
15403
  end
13848
15404
  @lasth = @font_size * @cell_height_ratio
13849
- when 'dt'
13850
- @lispacer = ''
13851
- addHTMLVertSpace(0, 0, cell, firstorlast)
13852
- when 'dd'
13853
- @lispacer = ''
13854
- if @rtl
13855
- @r_margin -= @listindent
13856
- else
13857
- @l_margin -= @listindent
15405
+ if (@page == @numpages - 1) and @pageopen[@numpages]
15406
+ # remove last blank page
15407
+ deletePage(@numpages)
13858
15408
  end
13859
- @listindentlevel -= 1
13860
- addHTMLVertSpace(0, 0, cell, firstorlast)
13861
- when 'ul', 'ol'
13862
- @listnum -= 1
13863
- @lispacer = ''
13864
- if @rtl
13865
- @r_margin -= @listindent
13866
- else
13867
- @l_margin -= @listindent
15409
+ if !@thead_margins['top'].nil?
15410
+ # restore top margin
15411
+ @t_margin = @thead_margins['top']
15412
+ @pagedim[@page]['tm'] = @t_margin
13868
15413
  end
13869
- @listindentlevel -= 1
13870
- if @listnum <= 0
13871
- @listnum = 0
13872
- addHTMLVertSpace(hbz, hb, cell, firstorlast)
13873
- else
13874
- addHTMLVertSpace(0, 0, cell, firstorlast)
15414
+ if table_el['attribute']['nested'].nil? or (table_el['attribute']['nested'] != 'true')
15415
+ # reset main table header
15416
+ @thead = ''
15417
+ @thead_margins = {}
13875
15418
  end
13876
- @lasth = @font_size * @cell_height_ratio
13877
- when 'li'
13878
- @lispacer = ''
15419
+ end
15420
+ if tag['block']
15421
+ unless dom[(dom[key]['parent'])]['attribute']['tablehead'] and dom[(dom[key]['parent'])]['attribute']['tablehead'] == "1" ### fix ###
15422
+ addHTMLVertSpace(hbz / 2, 0, cell, (dom[key+1].nil? or (dom[key+1]['value'] != 'table'))) ### fix ###
15423
+ end
15424
+ end
15425
+ when 'a'
15426
+ @href = {}
15427
+ @html_anchor = nil
15428
+ when 'sup'
15429
+ SetXY(GetX(), GetY() + (0.7 * parent['fontsize'] / @k))
15430
+ when 'sub'
15431
+ SetXY(GetX(), GetY() - (0.3 * parent['fontsize'] / @k))
15432
+ when 'div'
15433
+ addHTMLVertSpace(hbz, 0, cell, firstorlast)
15434
+ when 'blockquote'
15435
+ if @rtl
15436
+ @r_margin -= @listindent
15437
+ else
15438
+ @l_margin -= @listindent
15439
+ end
15440
+ @listindentlevel -= 1
15441
+ addHTMLVertSpace(hbz, hb, cell, firstorlast)
15442
+ when 'p'
15443
+ addHTMLVertSpace(hbz, hb, cell, firstorlast)
15444
+ when 'pre'
15445
+ addHTMLVertSpace(hbz, hb, cell, firstorlast)
15446
+ @premode = false
15447
+ when 'dl'
15448
+ @listnum -= 1
15449
+ if @listnum <= 0
15450
+ @listnum = 0
15451
+ addHTMLVertSpace(hbz, hb, cell, firstorlast)
15452
+ else
13879
15453
  addHTMLVertSpace(0, 0, cell, firstorlast)
13880
- when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
15454
+ end
15455
+ @lasth = @font_size * @cell_height_ratio
15456
+ when 'dt'
15457
+ @lispacer = ''
15458
+ @li_position_x = nil
15459
+ addHTMLVertSpace(0, 0, cell, firstorlast)
15460
+ when 'dd'
15461
+ @lispacer = ''
15462
+ @li_position_x = nil
15463
+ if @rtl
15464
+ @r_margin -= @listindent
15465
+ else
15466
+ @l_margin -= @listindent
15467
+ end
15468
+ @listindentlevel -= 1
15469
+ addHTMLVertSpace(0, 0, cell, firstorlast)
15470
+ when 'ul', 'ol'
15471
+ @listnum -= 1
15472
+ @lispacer = ''
15473
+ @li_position_x = nil
15474
+ if @rtl
15475
+ @r_margin -= @listindent
15476
+ else
15477
+ @l_margin -= @listindent
15478
+ end
15479
+ @listindentlevel -= 1
15480
+ if @listnum <= 0
15481
+ @listnum = 0
13881
15482
  addHTMLVertSpace(hbz, hb, cell, firstorlast)
15483
+ else
15484
+ addHTMLVertSpace(0, 0, cell, firstorlast)
15485
+ end
15486
+ @lasth = @font_size * @cell_height_ratio
15487
+ when 'li'
15488
+ @lispacer = ''
15489
+ @li_position_x = nil
15490
+ addHTMLVertSpace(0, 0, cell, firstorlast)
15491
+ when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
15492
+ addHTMLVertSpace(hbz, hb, cell, firstorlast)
15493
+ when 'form' # Form fields (since 4.8.000 - 2009-09-07)
15494
+ @form_action = ''
15495
+ @form_enctype = 'application/x-www-form-urlencoded'
13882
15496
  end
15497
+
13883
15498
  if dom[(dom[key]['parent'])]['attribute']['pagebreakafter']
13884
15499
  pba = dom[(dom[key]['parent'])]['attribute']['pagebreakafter']
13885
15500
  # check for pagebreak
@@ -14114,11 +15729,12 @@ protected
14114
15729
  #
14115
15730
  def putHtmlListBullet(listdepth, listtype='', size=10)
14116
15731
  size /= @k
14117
- fill = ''
15732
+ fill = +''
14118
15733
  color = @fgcolor
14119
15734
  width = 0
14120
15735
  textitem = ''
14121
15736
  tmpx = @x
15737
+ @x = @li_position_x
14122
15738
  lspace = GetStringWidth(' ')
14123
15739
  if listtype == '!'
14124
15740
  # set default list type for unordered list
@@ -14132,7 +15748,7 @@ protected
14132
15748
  # unordered types
14133
15749
  when 'none'
14134
15750
  when 'disc', 'circle'
14135
- fill = 'F' if listtype == 'disc'
15751
+ fill = +'F' if listtype == 'disc'
14136
15752
  fill << 'D'
14137
15753
  r = size / 6.0
14138
15754
  lspace += 2 * r
@@ -14190,6 +15806,7 @@ protected
14190
15806
  end
14191
15807
  @x = tmpx
14192
15808
  @lispacer = ''
15809
+ @li_position_x = nil
14193
15810
  end
14194
15811
 
14195
15812
  #
@@ -14206,6 +15823,7 @@ protected
14206
15823
  'rMargin' => @r_margin,
14207
15824
  'lMargin' => @l_margin,
14208
15825
  'cMargin' => @c_margin,
15826
+ 'tMargin' => @t_margin,
14209
15827
  'LineWidth' => @line_width,
14210
15828
  'linestyleWidth' => @linestyle_width,
14211
15829
  'linestyleCap' => @linestyle_cap,
@@ -14226,24 +15844,31 @@ protected
14226
15844
  'listordered' => @listordered,
14227
15845
  'listcount' => @listcount,
14228
15846
  'lispacer' => @lispacer,
14229
- 'lasth' => @lasth
15847
+ 'li_position_x' => @li_position_x,
15848
+ 'lasth' => @lasth,
15849
+ 'h' => @h,
15850
+ 'w' => @w,
15851
+ 'x' => @x,
15852
+ 'y' => @y,
14230
15853
  }
14231
15854
  return grapvars
14232
15855
  end
14233
15856
 
14234
15857
  #
14235
15858
  # Set graphic variables.
14236
- # [@param :gvars] array graphic variables
15859
+ # [@param array :gvars] graphic variables
15860
+ # [@param bool :option] set additional parameters
14237
15861
  # [@access protected]
14238
15862
  # [@since 4.2.010 (2008-11-14)]
14239
15863
  #
14240
- def setGraphicVars(gvars)
15864
+ def setGraphicVars(gvars, option = false)
14241
15865
  @font_family = gvars['FontFamily']
14242
15866
  @font_style = gvars['FontStyle']
14243
15867
  @font_size_pt = gvars['FontSizePt']
14244
15868
  @r_margin = gvars['rMargin']
14245
15869
  @l_margin = gvars['lMargin']
14246
15870
  @c_margin = gvars['cMargin']
15871
+ @t_margin = gvars['tMargin']
14247
15872
  @line_width = gvars['LineWidth']
14248
15873
  @linestyle_width = gvars['linestyleWidth']
14249
15874
  @linestyle_cap = gvars['linestyleCap']
@@ -14264,7 +15889,14 @@ protected
14264
15889
  @listordered = gvars['listordered']
14265
15890
  @listcount = gvars['listcount']
14266
15891
  @lispacer = gvars['lispacer']
14267
- #@lasth = gvars['lasth']
15892
+ @li_position_x = gvars['li_position_x']
15893
+ if option
15894
+ @lasth = gvars['lasth']
15895
+ @h = gvars['h']
15896
+ @w = gvars['w']
15897
+ @x = gvars['x']
15898
+ @y = gvars['y']
15899
+ end
14268
15900
  out('' + @linestyle_width + ' ' + @linestyle_cap + ' ' + @linestyle_join + ' ' + @linestyle_dash + ' ' + @draw_color + ' ' + @fill_color + '')
14269
15901
  unless empty_string(@font_family)
14270
15902
  SetFont(@font_family, @font_style, @font_size_pt)
@@ -14329,7 +15961,7 @@ protected
14329
15961
  #
14330
15962
  def readDiskCache(filename)
14331
15963
  filename = filename.path
14332
- data = ''
15964
+ data = +''
14333
15965
  open( filename,'rb') do |f|
14334
15966
  data << f.read()
14335
15967
  end
@@ -14671,18 +16303,17 @@ public
14671
16303
  #global jfrompage, jtopage
14672
16304
  #jfrompage = frompage
14673
16305
  #jtopage = topage
14674
- #@javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
14675
- #create_function('$matches', 'global $jfrompage, $jtopage;
14676
- # pagenum = matches[3].to_i + 1
14677
- # if (pagenum >= jtopage) and (pagenum < jfrompage)
14678
- # newpage = pagenum + 1
14679
- # elsif pagenum == jfrompage
14680
- # newpage = jtopage
14681
- # else
14682
- # newpage = pagenum
14683
- # end
14684
- # newpage -= 1
14685
- # return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
16306
+ #tmpjavascript =~ /this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/
16307
+ #pagenum = $3.to_i + 1
16308
+ #if (pagenum >= jtopage) && (pagenum < jfrompage)
16309
+ # newpage = pagenum + 1
16310
+ #elsif pagenum == jfrompage
16311
+ # newpage = jtopage
16312
+ #else
16313
+ # newpage = pagenum
16314
+ #end
16315
+ #newpage -= 1
16316
+ #@javascript = "this.addField(\'" + $1 + "\',\'" + $2 + "\'," + newpage + ""
14686
16317
 
14687
16318
  # return to last page
14688
16319
  lastPage(true)
@@ -15156,7 +16787,7 @@ public
15156
16787
  if this_self
15157
16788
  objvars = @objcopy.instance_variables
15158
16789
  objvars.each {|key|
15159
- eval("#{key} = @objcopy.instance_variable_get(key)") if key.to_s != '@objcopy'
16790
+ instance_variable_set(key, @objcopy.instance_variable_get(key)) if key.to_s != '@objcopy'
15160
16791
  }
15161
16792
  end
15162
16793
  return @objcopy
@@ -15293,7 +16924,7 @@ public
15293
16924
  # [@param string :default] default style
15294
16925
  # [@param boolean :mode] if true enable rasterization, false otherwise.
15295
16926
  # [@author] Nicola Asuni
15296
- # [@access protected:
16927
+ # [@access protected]
15297
16928
  # [@since 5.0.000 (2010-04-30)]
15298
16929
  #
15299
16930
  def getPathPaintOperator(style, default='S')