File Coverage

blib/lib/MARC/Moose/Formater/UnimarcToMarc21.pm
Criterion Covered Total %
statement 17 45 37.7
branch 0 22 0.0
condition 0 3 0.0
subroutine 6 8 75.0
pod 0 1 0.0
total 23 79 29.1


line stmt bran cond sub pod time code
1             package MARC::Moose::Formater::UnimarcToMarc21;
2             $MARC::Moose::Formater::UnimarcToMarc21::VERSION = '1.0.49';
3             # ABSTRACT: Convert biblio record from UNIMARC to MARC21
4 4     4   29 use Moose;
  4         9  
  4         37  
5              
6 4     4   32195 use 5.010;
  4         16  
7 4     4   25 use utf8;
  4         8  
  4         42  
8              
9             extends 'MARC::Moose::Formater';
10              
11 4     4   296 use List::Util qw/ first /;
  4         14  
  4         416  
12 4     4   28 use MARC::Moose::Field::Control;
  4         11  
  4         141  
13 4     4   17 use MARC::Moose::Field::Std;
  4         9  
  4         52430  
14              
15              
16             # Equivalence UNIMARC author type code > MARC21
17             # Each UNIMARC code points to a array ref which first entry contains MARC21
18             # code and second MARC21 author type description. The second entry isn't used
19             # yet.
20             my %authcode = map { /^(\d*) (\w*) (.*)$/; $1 => [$2, $3] } split /\n/, <<EOS;
21             005 act actor
22             010 adp adapter
23             020 ann annotator
24             030 arr arranger
25             040 art artist
26             050 asg assignee
27             060 asn associated name
28             065 auc auctioneer
29             070 aut author
30             072 aqt author in quotations or text abstract
31             075 aft author of afterword, colophon, etc.
32             080 aui author of introd
33             090 aus author of screenplay
34             100 ant bibl. antecedent
35             110 bnd binder
36             120 bdd binding designer
37             130 bkd book designer
38             140 bjd bkjacket designer
39             150 bpd bkplate designer
40             160 bsl bookseller
41             170 cll calligrapher
42             180 ctg cartographer
43             190 cns censor
44             200 chr choreographer
45             205 clb collaborator
46             210 cmm commentator
47             212 cwt commentator for written text
48             220 com compiler
49             230 cmp composer
50             240 cmt compositor
51             245 ccp conceptor
52             250 cnd conductor
53             255 csp consultant to a project
54             260 cph copyright holder
55             270 crr corrector
56             273 cur curator
57             275 dnc dancer
58             280 dte dedicatee
59             290 dto dedicator
60             295 dgg degree grantor
61             300 drt director
62             305 dis dissertant
63             310 dst distributor
64             320 dnr donor
65             330 dub dubious author
66             340 edt editor
67             350 egr engraver
68             360 etr etcher
69             365 exp expert
70             370 flm film editor
71             380 frg forger
72             390 fmo former owner
73             400 fnd funder
74             410 grt graphic technician
75             420 hnr honoree
76             430 ilu illuminator
77             440 ill illustrator
78             450 ins inscriber
79             460 ive interviewee
80             470 ivr interviewer
81             480 lbt librettist
82             490 lse licensee
83             500 lso licensor
84             510 ltg lithographer
85             520 lyr lyricist
86             530 mte metal engraver
87             540 mon monitor/contractor
88             545 mus musician
89             550 nrt narrator
90             555 opn opponent
91             557 orm organizer of meeting
92             560 org originator
93             570 oth other
94             580 ppm papermaker
95             582 pta patent applicant
96             584 inv inventor
97             587 pth patent holder
98             590 prf performer
99             595 res research
100             600 pht photographer
101             610 prt printer
102             620 pop printer of plates
103             630 pro producer
104             635 prg programmer
105             640 pfr proofreader
106             650 pbl publisher
107             651 pbd publishing director
108             660 rcp recipient
109             670 rce recording engineer
110             673 rth research team head
111             675 rev reviewer
112             677 rtm research team member
113             680 rbr rubricator
114             690 sce scenarist
115             695 sad scientific advisor
116             700 scr scribe
117             705 scl sulptor
118             710 sec secretary
119             720 sgn signer
120             721 sng singer
121             723 spn sponsor
122             725 stn standards body
123             727 ths thesis advisor
124             730 trl translator
125             740 tyd type designer
126             750 tyg typographer
127             755 voc vocalist
128             760 wde wood engraver
129             770 wam writer of accompanying material
130             EOS
131              
132             # UNIMARC 100 Type of pub
133             my %typeofpub = map { /(\w) (\w)/; $1 => $2; } split /\n/, <<EOS;
134             a c
135             b d
136             c u
137             d s
138             e r
139             f q
140             g m
141             h t
142             i p
143             j e
144             EOS
145              
146             # UNIMARC 100 Target Audience Code
147             my %target_audience = map { /(\w|\|) (\w|\|)/; $1 => $2; } split /\n/, <<EOS;
148             b a
149             c b
150             a j
151             d c
152             e d
153             k e
154             m g
155             | |
156             EOS
157              
158             # List of moved fields unchanged
159             my @unchanged;
160             push @unchanged, [$_, 500] for 300..315;
161             push @unchanged, [317, 561],
162             [320, 504],
163             [321, 500],
164             [322, 508],
165             [323, 511],
166             [324, 500],
167             [328, 502],
168             [330, 520],
169             [332, 524],
170             [333, 521],
171             [337, 538],
172             [371, 506],
173             [686, '084'];
174              
175             # Tags with non-filing indicator (pos 1 or 2)
176             my $nonfiling_tags = [
177             [ qw/130 630 730 740 830/ ],
178             [ qw/240 242 243 245 440 830/ ],
179             ];
180              
181             # NSB/NSE characters
182             my $ns_characters = [
183             [ "\x08", "\x09" ],
184             [ "\x88", "\x89" ]
185             ];
186              
187              
188              
189             # Procedure 4 Title
190             sub procedure_title {
191 0     0 0   my ($self, $subf) = @_;
192              
193 0           my @sf;
194 0           my ($h_index) = (-1);
195 0           my @equivals = (
196             [ 'a', 'a' ],
197             [ 'j', 'f' ],
198             [ 'n', 'g' ],
199             [ 'h', 'n', '.' ],
200             [ 'k', 'f', '.' ],
201             [ 'l', 'k', '.' ],
202             [ 'm', 'l', '.' ],
203             [ 'q', 's', '.' ],
204             [ 'r', 'r', ',' ],
205             [ 's', 's', ',' ],
206             [ 't', 'o', ';' ],
207             [ 'u', 'r', ',' ],
208             [ 'x', 'x', ',' ],
209             );
210 0           for ( @$subf ) {
211 0           my ($letter, $value) = @$_;
212 0 0   0     if ( my $equival = first { $_->[0] eq $letter } @equivals ) {
  0            
213 0           my ($from, $to, $sep) = @$equival;
214 0 0 0       if ( $sep && @sf ) {
215 0           my $match = $sep;
216 0 0         $match = '\.' if $match eq '.';
217 0 0         if ( $sf[-1]->[0] !~ /$match$/ ) {
218 0           $sf[-1]->[1] .= $sep;
219             }
220             }
221 0           push @sf, [ $to => $value ];
222             }
223             else {
224 0           for ($letter) {
225 0 0         if ( /e/ ) {
    0          
226 0 0         next unless @sf; #FIXME warning required
227 0 0         if ( $sf[-1][0] =~ /a|n|p/ ) {
228 0           $sf[-1]->[1] .= ' :';
229 0           push @sf, [ b => $value ];
230             }
231             else {
232 0           $sf[-1]->[1] .= " : $value";
233             }
234             }
235             elsif ( /i/ ) {
236 0 0         if ( @sf ) {
237 0 0         if ( $sf[-1]->[0] eq 'h' ) {
238 0 0         $sf[-1]->[1] .= ',' if $sf[-1]->[1] !~ /,$/;
239             }
240             else {
241 0           $sf[-1]->[1] .= '.';
242             }
243             }
244 0           push @sf, [ p => $value ];
245             }
246             }
247             }
248             }
249              
250 0           return \@sf;
251             }
252              
253              
254             override 'format' => sub {
255             my ($self, $unimarc) = @_;
256              
257             my $record = MARC::Moose::Record->new();
258              
259             $record->_leader(" nam a22 7a 4500");
260              
261             my $code008 = '120130t xxu||||| |||| 00| 0 ||| d';
262              
263             my @sf040;
264              
265             # 001 => 001
266             for my $field ( $unimarc->field('001' ) ) {
267             $record->append($field->clone());
268             }
269              
270             # ISBN 010 => 020
271             for my $field ( $unimarc->field('010') ) {
272             my @sf;
273             for ( @{$field->subf} ) {
274             my ($letter, $value) = @$_;
275             for ($letter) {
276             if ( /a|z/ ) {
277             $value =~ s/-//g;
278             push @sf, [ $letter => $value ];
279             }
280             elsif ( /b/ ) {
281             $value = "($value)" unless $value =~ /^\(/;
282             if (@sf) {
283             $sf[-1]->[1] .= " $value";
284             }
285             else {
286             push @sf, [ c => $value ];
287             }
288             }
289             elsif ( /d/ ) {
290             if (@sf) {
291             $sf[-1]->[1] .= " :";
292             }
293             push @sf, [ c => $value ];
294             }
295             }
296             }
297             $record->append( MARC::Moose::Field::Std->new(
298             tag => '020', subf => \@sf ) );
299             }
300              
301             # ISSN 011 => 022
302             # Except 011$b$d => 365
303             for my $field ( $unimarc->field('011') ) {
304             my (@sf, @price);
305             for ( @{$field->subf} ) {
306             my ($letter, $value) = @$_;
307             for ($letter) {
308             if ( /a/ ) {
309             $value =~ s/-//g;
310             push @sf, [ a => $value ];
311             }
312             elsif ( /z/ ) {
313             $value =~ s/-//g;
314             push @sf, [ y => $value ];
315             }
316             elsif ( /b|d/ ) {
317             $value = "($value)" unless $value =~ /^\(/;
318             my $newlet = $letter eq 'b' ? 'b' : 'd';
319             push @price, [ $newlet => $value ];
320             }
321             }
322             }
323             $record->append( MARC::Moose::Field::Std->new(
324             tag => '022', subf => \@sf ) ) if @sf;
325             $record->append(MARC::Moose::Field::Std->new(
326             tag => '365', subf => \@price ) ) if @price;
327             }
328              
329             # EAN 076 => 024. Get only $a subfield
330             for my $field ( $unimarc->field('073') ) {
331             my $value = $field->subfield('a');
332             next unless $value;
333             $record->append( MARC::Moose::Field::Std->new(
334             tag => '024', subf => [ [ a => $value ] ] ) );
335             }
336              
337             # 100 => 008
338             if ( my $field = $unimarc->field('100') ) {
339             my $code100 = $field->subfield('a');
340             if ( $code100 && length($code100) > 20 ) {
341             # Date entered on file
342             substr $code008, 0, 6, substr($code100, 2, 6);
343              
344             # Type of publication date
345             my $value = substr($code100, 8, 1);
346             $value = $typeofpub{$value} || ' ';
347             substr $code008, 6, 1, $value;
348              
349             # Date 1
350             $value = substr($code100, 9, 4);
351             if ( 1 ) { #FIXME Determine if it's a serials
352             # Not serials
353             my $count = 0;
354             for ( split //, $value ) { $count++ if / /; }
355             $value =~ s/ /0/g if $count <= 3;
356             }
357             else {
358             # A serials
359             $value =~ s/ /u/g;
360             }
361             substr $code008, 7, 4, $value;
362              
363             # Date 2
364             $value = substr($code100, 13, 4);
365             if ( 1 ) { #FIXME Determine if it's a serials
366             # Not serials
367             my $count = 0;
368             for ( split //, $value ) { $count++ if / /; }
369             $value =~ s/ /0/g if $count <= 3;
370             }
371             else {
372             # A serials
373             $value =~ s/ /u/g;
374             }
375             substr $code008, 11, 4, $value;
376              
377             # 3 positions for target audience
378             $value = substr($code100, 17, 3);
379             for (my $i=0; $i < 3; $i++) {
380             $value = substr($code100, 17+$i, 1);
381             $value = $target_audience{$value} || ' ';
382             substr $code008, 17+$i, 1, $value;
383             }
384            
385             # Language of cataloging
386             push @sf040, [ b => substr($code100, 22, 3) ];
387              
388             # Alphabet of title, converted if serials
389             # FIXME
390             if ( 0 ) {
391             substrr $code008, 33, 1, substr($code100,34,1);
392             }
393             }
394             }
395              
396             # Language 101 => 041 and 008
397             if ( my $field = $unimarc->field('101') ) {
398             # FIXME: à virer
399             if ( ref($field) eq 'MARC::Moose::Field::Control' ) {
400             say $unimarc->as('Text');
401             exit;
402             }
403             my @all = @{$field->subf};
404             my $count_a = 0;
405             my (@sf, @sf_b);
406             for (@all) {
407             my ($letter, $value) = @$_;
408             for ($letter) {
409             if ( /a/ ) {
410             next if $count_a >= 6;
411             $count_a++;
412             if ( $count_a == 1 ) {
413             $value .= ' ';
414             $value = substr($value, 0, 3);
415             substr $code008, 35, 3, $value;
416             }
417             push @sf, [ a => $value];
418             }
419             elsif ( /c/ ) { push @sf, [ h => $value ]; }
420             elsif ( /b/ ) { push @sf_b, $value; }
421             elsif ( /d/ ) { push @sf, [ b => $value ]; }
422             elsif ( /e/ ) { push @sf, [ f => $value ]; }
423             elsif ( /f|g/ ) { }
424             elsif ( /j/ ) { push @sf, [ b => $value ]; }
425             elsif ( /h/ ) { push @sf, [ e => $value ]; }
426             elsif ( /i/ ) { push @sf, [ g => $value ]; }
427             }
428             }
429             if ( @sf_b ) {
430             for ( @sf ) {
431             if ($_->[0] eq 'h') {
432             $_->[1] .= ' ' . join(' ', @sf_b);
433             last;
434             }
435             }
436             }
437             my $ind1 = $field->ind1;
438             $ind1 = '0' if $ind1 eq ' ';
439             $ind1 = '1' if $ind1 eq '2';
440             $record->append( MARC::Moose::Field::Std->new(
441             tag => '041',
442             ind1 => $ind1,
443             subf => \@sf ) );
444             }
445             else {
446             substr($code008, 35, 3) = '|||';
447             }
448              
449             # 125 => 008
450             # FIXME: 125$b isn't handled at all
451             if ( my $field = $unimarc->field('125') ) {
452             my $value = $field->subfield('a');
453             my ($pos0, $pos1);
454             $pos0 = substr($value, 0, 1) if $value && length($value) >= 1;
455             $pos1 = substr($value, 1, 1) if $value && length($value) >= 2;
456             $pos0 ||= '|';
457             $pos0 = 'n' if $pos0 eq 'x';
458             $pos1 ||= '|';
459             $pos1 = 'n' if $pos1 eq 'x';
460             $pos1 = ' ' if $pos1 eq 'y';
461             substr($code008, 20, 2) = $pos0 . $pos1;
462             }
463              
464             $record->append( MARC::Moose::Field::Control->new(
465             tag => '008', value => $code008 ) );
466              
467             # Title
468             for my $field ( $unimarc->field('200') ) {
469             my @sf;
470             my ($a_index, $h_index) = (-1, -1);
471             SUBFIELD200:
472             for ( @{$field->subf} ) {
473             my ($letter, $value) = @$_;
474             for ($letter) {
475             if ( /a/ ) {
476             if ( $a_index == -1 ) {
477             push @sf, [ a => $value ];
478             $a_index = $#sf;
479             }
480             else {
481             $sf[$a_index]->[1] .= " ; $value";
482             }
483             }
484             elsif ( /b/) {
485             if ( $h_index == -1 ) {
486             push @sf, [ h => $value ];
487             $h_index = $#sf;
488             }
489             else {
490             if ( $#sf == $h_index ) {
491             $sf[$h_index]->[1] .= " + $value";
492             }
493             else {
494             $sf[-1]->[1] .= " ($value)";
495             }
496             }
497             }
498             elsif ( /c/ ) {
499             next SUBFIELD200 unless @sf; #FIXME warning required
500             $sf[-1]->[1] .= ". $value";
501             }
502             elsif ( /d/ ) {
503             next SUBFIELD200 unless @sf; #FIXME warning required
504             if ( $sf[-1]->[0] =~ /a|n|p/ ) {
505             $sf[-1]->[1] .= ' =';
506             $value =~ s/^= //;
507             push @sf, [ b => $value ];
508             }
509             else {
510             $sf[-1]->[1] .= " = $value";
511             }
512             }
513             elsif ( /e/ ) {
514             next SUBFIELD200 unless @sf; #FIXME warning required
515             if ( $sf[-1]->[0] =~ /a|n|p/ ) {
516             $sf[-1]->[1] .= ' :';
517             push @sf, [ b => $value ];
518             }
519             else {
520             $sf[-1]->[1] .= " : $value";
521             }
522             }
523             elsif ( /f/) {
524             next SUBFIELD200 unless @sf; #FIXME warning required
525             if ( $sf[-1]->[0] =~ /a|b|n|p/ ) {
526             $sf[-1]->[1] .= ' /';
527             push @sf, [ c => $value ];
528             }
529             else {
530             $sf[-1]->[1] .= " / $value";
531             }
532             }
533             elsif ( /g/) {
534             next SUBFIELD200 unless @sf; #FIXME warning required
535             $sf[-1]->[1] .= " ; $value";
536             }
537             elsif ( /h/ ) {
538             next SUBFIELD200 unless @sf; #FIXME warning required
539             if ( $sf[-1]->[0] =~ /a|n|p/ ) {
540             $sf[-1]->[1] .= '.';
541             push @sf, [ n => $value ];
542             }
543             else {
544             #$sf[-1]->[1] .= ". $value";
545             push @sf, [ n => $value ];
546             }
547             }
548             elsif ( /i/ ) {
549             next SUBFIELD200 unless @sf; #FIXME warning required
550             if ( @sf && $sf[-1]->[0] =~ /a|n|p/ ) {
551             $sf[-1]->[1] .= ',';
552             push @sf, [ p => $value ];
553             }
554             else {
555             $sf[-1]->[1] .= ". $value";
556             }
557             }
558             elsif ( /v|z|5|6|7/ ) { next SUBFIELD200 }
559             }
560             }
561             next unless @sf;
562             $sf[$h_index]->[1] = '[' . $sf[$h_index]->[1] . ']' unless $h_index == -1;
563             # Point final
564             if (@sf) {
565             my $last_value = $sf[-1][1];
566             my $last_char = substr($last_value, length($last_value)-1);
567             $sf[-1][1] = "$last_value." if $last_char !~ /[.?,;:]/;
568             }
569              
570             # Indicators
571             my ($ind1, $ind2) = ($field->ind1, 0);
572             for ($ind1) {
573             if ( /0/ ) { }
574             elsif ( /1/ ) {
575             #FIXME Test marc21 100/110/111/130 presence
576             $ind1 = $unimarc->field('700|710' ) ? 1 : 0;
577             }
578             else { $ind1 = 1; }
579             }
580             $record->append( MARC::Moose::Field::Std->new(
581             tag => '245', ind1 => $ind1, ind2 => $ind2,
582             subf => \@sf ) );
583             }
584            
585             # TODO 204
586              
587             # 205 => 250
588             for my $field ($unimarc->field('205') ) {
589             my @sf;
590             my ($a_index, $b_index) = (-1, -1);
591             for ( @{$field->subf} ) {
592             my ($letter, $value) = @$_;
593             for ($letter) {
594             if ( /a/ ) {
595             if ( $a_index == -1 ) {
596             push @sf, [ a => $value ];
597             $a_index = $#sf;
598             }
599             else {
600             $sf[$a_index]->[1] .= ", $value";
601             }
602             }
603             elsif ( /b/ ) {
604             if ( @sf ) {
605             $sf[-1]->[1] .= ", $value";
606             }
607             else {
608             push @sf, [ a => $value ];
609             $a_index = $#sf;
610             }
611             }
612             elsif ( /d/ ) {
613             if ( $b_index == -1 ) {
614             push @sf, [ b => $value];
615             $b_index = $#sf;
616             }
617             else {
618             $sf[-1]->[1] .= " $value";
619             }
620             }
621             elsif ( /f/ ) {
622             if ( $b_index == -1 ) {
623             $sf[-1]->[1] .= " / " if @sf;
624             push @sf, [ b => $value];
625             $b_index = $#sf;
626             }
627             else {
628             $sf[-1]->[1] .= " / $value";
629             }
630             }
631             elsif ( /g/ ) {
632             if ( @sf ) { $sf[-1]->[1] .= " / $value"; }
633             else { push @sf, [ a => $value ] }
634             }
635             }
636             }
637             next unless @sf;
638             if ( $b_index >= 1 ) {
639             my $value = $sf[$b_index]->[1];
640             if ( $value =~ /= $/ ) {
641             $value =~ s/= $//;
642             $sf[$b_index]->[1] = $value;
643             $sf[$b_index-1]->[1] .= '= ';
644             }
645             }
646             # Point final
647             $sf[-1][1] = $sf[-1][1] . '.' if @sf && $sf[-1][1] !~ /\.$/;
648             $record->append( MARC::Moose::Field::Std->new(
649             tag => '250', ind1 => $field->ind1, ind2 => $field->ind2,
650             subf => \@sf ) );
651             }
652              
653             # TODO 206
654              
655             # 207 => 362
656             for my $field ($unimarc->field('207') ) {
657             my @sf;
658             my $a_index = -1;
659             for ( @{$field->subf} ) {
660             my ($letter, $value) = @$_;
661             for ($letter) {
662             if ( /a/ ) {
663             if ( $a_index == -1 ) {
664             push @sf, [ a => $value ];
665             $a_index = $#sf;
666             }
667             else {
668             my $prev = $sf[$a_index]->[1];
669             $prev =~ s/ *$//;
670             $prev =~ s/;$//;
671             $prev =~ s/ *$//;
672             $sf[$a_index]->[1] = "$prev ; $value";
673             }
674             }
675             elsif ( /v/ ) {
676             push @sf, [ z => $value ];
677             }
678             }
679             }
680             next unless @sf;
681             # Point at the end
682             $sf[-1][1] = $sf[-1][1] . '.' if @sf && $sf[-1][1] !~ /\.$/;
683             $record->append( MARC::Moose::Field::Std->new(
684             tag => '362', ind2 => $field->ind1,
685             subf => \@sf ) );
686             }
687              
688             #TODO 208
689              
690             # 210/214 => 260
691             for my $field ( $unimarc->field('210|214') ) {
692             my @sf;
693             for ( @{$field->subf} ) {
694             my ($letter, $value) = @$_;
695             $value =~ s/^ *//, $value =~ s/ *$//;
696             my %found;
697             for ($letter) {
698             if ( /a/ ) {
699             push @sf, [ a => $value ];
700             }
701             elsif ( /b/ ) {
702             $value = "($value)" if $value !~ /^\(/;
703             if ( @sf ) {
704             $sf[-1]->[1] .= " $value";
705             }
706             else {
707             push @sf, [ a => $value ];
708             }
709             }
710             elsif ( /c/ ) {
711             push @sf, [ b => $value ];
712             }
713             elsif ( /d/ ) {
714             push @sf, [ c => $value ];
715             }
716             elsif ( /e/ ) {
717             push @sf, [ e => $value ];
718             }
719             elsif ( /f/ ) {
720             unless ( $found{$letter} ) {
721             $found{$letter} = 1;
722             $sf[-1]->[1] .= ", $value" if @sf;
723             }
724             }
725             elsif ( /g/ ) {
726             unless ( $found{$letter} ) {
727             $found{$letter} = 1;
728             push @sf, [ f => $value ];
729             }
730             }
731             elsif ( /h/ ) {
732             unless ( $found{$letter} ) {
733             $found{$letter} = 1;
734             push @sf, [ g => $value ];
735             }
736             }
737             elsif ( /j/ ) {
738             $record->append( MARC::Moose::Field::Std->new(
739             tag => '265', subf => [ a => $value ] ) );
740             }
741             elsif ( /k/ ) {
742             $record->append( MARC::Moose::Field::Std->new(
743             tag => '265', ind1 => '0', ind2 => '0',
744             subf => [ a => $value ] ) );
745             }
746             elsif ( /l/ ) {
747             $record->append( MARC::Moose::Field::Std->new(
748             tag => '265', ind1 => '1', ind2 => '0',
749             subf => [ [ a => $value ] ] ) );
750             }
751             elsif ( /m/ ) {
752             $record->append( MARC::Moose::Field::Std->new(
753             tag => '265', ind1 => '2', ind2 => '0',
754             subf => [ a => $value ] ) );
755             }
756             }
757             }
758             next unless @sf;
759             # Ponctuation
760             for (my $i=0; $i < @sf; $i++) {
761             my ($letter, $value) = @{$sf[$i]};
762             for ($letter) {
763             if ( /a/ ) {
764             $sf[$i-1]->[1] .= ' ;' if $i;
765             }
766             elsif ( /b|f/ ) {
767             $sf[$i-1]->[1] .= ' :' if $i;
768             }
769             elsif ( /c|g/ ) {
770             $sf[$i-1]->[1] .= ',' if $i;
771             }
772             }
773             $value = "($value)" if $letter =~ /e|f|g/;
774             if ( $value =~ /^= / ) {
775             $value =~ s/^= //;
776             $sf[$i-1]->[1] .= ' =' if $i;
777             }
778             $sf[$i]->[1] = $value;
779             }
780             $sf[-1][1] = $sf[-1][1] . '.' if @sf && $sf[-1][1] !~ /\.$/;
781             $record->append( MARC::Moose::Field::Std->new( tag => '260', subf => \@sf ) );
782             }
783              
784             # TODO 211 => 263
785              
786             # 215 => 300
787             for my $field ( $unimarc->field('215') ) {
788             my @sf;
789             SUBFIELD215:
790             for ( @{$field->subf} ) {
791             my ($letter, $value) = @$_;
792             $value =~ s/^ *//, $value =~ s/ *$//;
793             for ($letter) {
794             if ( /c/ ) { $letter = 'b'; }
795             elsif ( /d/ ) { $letter = 'c'; }
796             elsif ( /6|7/ ) { next SUBFIELD215; }
797             }
798             push @sf, [ $letter => $value ];
799             }
800             next unless @sf;
801             # Ponctuation
802             for (my $i=1; $i < @sf; $i++) {
803             my ($letter, $value) = @{$sf[$i]};
804             for ($letter) {
805             if ( /b/ ) { $sf[$i-1]->[1] .= ' :'; }
806             elsif ( /c/ ) { $sf[$i-1]->[1] .= ' ;'; }
807             elsif ( /e/ ) { $sf[$i-1]->[1] .= ' + '; }
808             }
809             }
810             $sf[-1][1] = $sf[-1][1] . '.' if $sf[-1][1] !~ /\.$/;
811             $record->append( MARC::Moose::Field::Std->new( tag => '300', subf => \@sf ) );
812             }
813              
814             # 225 => 490
815             for my $field ( $unimarc->field('225') ) {
816             my (@sf, @a, @vx);
817             my $prev_letter = '';
818             for ( @{$field->subf} ) {
819             my ($letter, $value) = @$_;
820             $value =~ s/^ *//, $value =~ s/ *$//;
821             $value =~ s/\x88//g, $value =~ s/\x89//;
822             for ($letter) {
823             if ( /a/ ) { push @a, $value; }
824             elsif ( /d/ ) { push @a, " = $value" }
825             elsif ( /e/ ) { push @a, " : $value" }
826             elsif ( /f/ ) { push @a, " / $value" }
827             elsif ( /h/ ) { push @a, ". $value" }
828             elsif ( /i/ ) {
829             push @a, $prev_letter eq 'h' ? ", $value " : ". $value";
830             }
831             elsif ( /v|x/ ) { push @vx, [ $letter => $value ] }
832             }
833             $prev_letter = $letter;
834             }
835             next unless @a;
836             push @sf, [ a => join('', @a) ];
837             push @sf, @vx;
838             $record->append( MARC::Moose::Field::Std->new(
839             tag => '490',
840             ind1 => $field->ind1 =~ /0|2/ ? 1 : 0,
841             subf => \@sf ) );
842             }
843              
844             # 230 => 256
845             for my $field ( $unimarc->field('230') ) {
846             $record->append($field->clone('256'));
847             }
848              
849             # Unchanged fields
850             for my $fromto ( @unchanged ) {
851             my ($from, $to) = @$fromto;
852             for my $field ( $unimarc->field($from) ) {
853             $record->append($field->clone($to));
854             }
855             }
856              
857             # 325 => 533
858             for my $field ( $unimarc->field('325') ) {
859             $record->append( MARC::Moose::Field::Std->new(
860             tag => '533',
861             subf => [ [ n => $field->subfield('a') ] ] ) );
862             }
863              
864             # 326 => 533
865             for my $field ( $unimarc->field('326') ) {
866             # FIXME Should be done depending on biblio record type:
867             # MAP, SERIALS
868             my $type = 'SERIALS';
869             my $new_field;
870             if ( $type =~ /SERIALS/ ) {
871             $new_field = $field->clone('310');
872             }
873             $record->append($new_field);
874             }
875              
876             # 327 => 505
877             for my $field ( $unimarc->field('327') ) {
878             my $ind1 = $field->ind1;
879             $ind1 = 0 if $ind1 =~ /1/;
880             $ind1 = 1 if $ind1 =~ /0/;
881             my @a = map { $_->[1] } @{$field->subf};
882             $record->append( MARC::Moose::Field::Std->new(
883             tag => '505', ind1 => $ind1,
884             subf => [ [ a => join(' ', @a) ] ] ) );
885             }
886              
887             # 329 => 505
888             # This is French (CCfr) specific field without equivalent in MARC21
889             # Concatained into 505 field
890             for my $field ( $unimarc->field('359') ) {
891             my @a = map { $_->[1] } @{$field->subf};
892             $record->append( MARC::Moose::Field::Std->new(
893             tag => '505', ind1 => '0',
894             subf => [ [ a => join(' -- ', @a) ] ] ) );
895             }
896              
897              
898             # 336 => 500
899             for my $field ( $unimarc->field('336') ) {
900             $record->append( MARC::Moose::Field::Std->new(
901             tag => '500',
902             subf => [ [ a => 'Type of computer file: ' . $field->subfield('a') ] ] ) );
903             }
904              
905             # 345 => 037
906             for my $field ( $unimarc->field('345') ) {
907             my @sf;
908             for ( @{$field->subf} ) {
909             my ($letter, $value) = @$_;
910             $letter = $letter eq 'a' ? 'b' :
911             $letter eq 'b' ? 'a' :
912             $letter eq 'c' ? 'f' :
913             $letter eq 'd' ? 'c' : $letter;
914             push @sf, [ $letter => $value ];
915             }
916             $record->append( MARC::Moose::Field::Std->new(
917             tag => '037', subf => \@sf ) );
918             }
919              
920             # TODO 410 411 421 422 423 430 431 432 433 434 435 436 437 440 441 442 443
921             # 444 445 446 447 448 451 452 453
922              
923             # 454 => 765
924             for my $ft ( (
925             [410, 760],
926             [411, 762],
927             [421, 770],
928             [422, 772],
929             [423, 777],
930             [430, 780, 0],
931             [431, 780, 1],
932             [432, 780, 2],
933             [433, 780, 3],
934             [434, 780, 5],
935             [435, 780, 6],
936             [436, 780, 4],
937             [437, 780, 7],
938             [440, 785, 0],
939             [441, 785, 1],
940             [442, 785, 2],
941             [443, 785, 3],
942             [444, 785, 4],
943             [445, 785, 5],
944             [446, 785, 6],
945             [447, 785, 7],
946             [448, 785, 8],
947             [451, 775],
948             [452, 776],
949             [453, 767],
950             [454, 765],
951             [455, 787, 8, 'Reproduction of:'],
952             [456, 787, 8, 'Reproduced as:'],
953             [461, 773],
954             [462, 774],
955             [463, 773],
956             [464, 774],
957             [470, 787, 8, 'Item reviewed:'],
958             [488, 787, 8, 'Reproduced as:'],
959             [491, 774],
960             [492, 774],
961             [493, 773],
962             [494, 773],
963             ) ) {
964             my ($from, $to, $ind2, $text) = @$ft;
965             $ind2 = ' ' unless $ind2;
966             for my $field ( $unimarc->field($from) ) {
967             my @sf;
968             push @sf, [ i => $text ] if $text;
969             for ( @{$field->subf} ) {
970             my ($letter, $value) = @$_;
971             if ( $letter eq 't') {
972             $value =~ s/\x{0088}//g;
973             $value =~ s/\x{0089}//g;
974             }
975             $letter = $letter eq '1' ? 'a' :
976             $letter eq '3' ? 'w' :
977             $letter eq 'v' ? 'g' :
978             $letter eq 'y' ? 'z' : $letter;
979             push @sf, [ $letter => $value ];
980             }
981             my $ind1 = $field->ind2 =~ /0/ ? 1 : 0;
982             $record->append( MARC::Moose::Field::Std->new(
983             tag => $to, ind1 => $ind1, ind2 =>$ind2, subf => \@sf ) );
984             }
985             }
986              
987             # 500 => 240 or 130
988             for my $field ( $unimarc->field('500|503|517|540|541') ) {
989             my ($ind1, $ind2) = ($field->ind1, $field->ind2);
990             my $tag = '240';
991             if ( $ind2 eq '0' ) {
992             $ind2 = 0;
993             }
994             elsif ( $ind2 eq '1' ) {
995             $tag = '130';
996             ($ind1, $ind2) = (0, ' ');
997             }
998             else {
999             ($ind1, $ind2) = (1, 0);
1000             }
1001             $record->append( MARC::Moose::Field::Std->new(
1002             tag => $tag, ind1 => $ind1, ind2 => $ind2,
1003             subf => $self->procedure_title($field->subf) ) );
1004             }
1005              
1006             # 545 => 773, on passe t en a
1007             for my $field ( $unimarc->field('545') ) {
1008             $field->tag('773');
1009             $field->subf( [ grep { $_->[0] = 't' if $_->[0] eq 'a'; $_ } @{$field->subf} ] );
1010             $record->append( $field );
1011             }
1012              
1013             # 600 => 600
1014             # Suppr 6 et 7. f => d
1015             for my $field ( $unimarc->field('600') ) {
1016             my @names;
1017             my $date;
1018             # Skip $6 and $7
1019             my @sf;
1020             my $date_available = 0;
1021             SUBFIELD600:
1022             for ( @{$field->subf} ) {
1023             my ($letter, $value) = @$_;
1024             $value =~ s/^ *//; $value =~ s/ *$//;
1025             next unless $value;
1026             for ($letter) {
1027             if ( /6|7/ ) { next SUBFIELD600; }
1028             elsif ( /a|b/ ) { push @names, $value; next; }
1029             elsif ( /f/ ) { $date_available = 1; $letter = 'd'; }
1030             elsif ( /y/ ) { $letter = 'z'; }
1031             elsif ( /z/ ) { $letter = 'y'; }
1032             push @sf, [ $letter => $value ];
1033             }
1034             }
1035             my @sf_complete;
1036             my $notpushed = 1;
1037             for (@sf) {
1038             my ($letter, $value) = @$_;
1039             if ($letter gt 'a' && $notpushed) {
1040             push @sf_complete, [ a => join(', ', @names) . ($date_available ? ',' : '') ];
1041             $notpushed = 0;
1042             }
1043             push @sf_complete, $_;
1044             }
1045             $record->append( MARC::Moose::Field::Std->new(
1046             tag => '600', subf => \@sf_complete ) );
1047             }
1048              
1049             # 605 => 630 - 606 => 650 - 607 => 651 - 608 => 650
1050             # On conserve à leur place les lettres a x j (subdivision de forme)
1051             # On inverse y et z. et déplacée en v.
1052             # On suppr les $3
1053             for my $fromto ( ( [601, 650], [604, 600], [605, 630], [606, 650], [607, 651], [608, 650] ) ) {
1054             my ($from, $to) = @$fromto;
1055             for my $field ( $unimarc->field($from) ) {
1056             my @sf;
1057             for ( @{$field->subf} ) {
1058             my ($letter, $value) = @$_;
1059             $value =~ s/^ *//, $value =~ s/ *$//;
1060             next if $letter =~ /3/;
1061             if ( $letter eq 'j' ) {
1062             $letter = 'v';
1063             }
1064             elsif ( $letter eq 'y' ) {
1065             $letter = 'z';
1066             }
1067             elsif ( $letter eq 'z' ) {
1068             $letter = 'y';
1069             }
1070             push @sf, [ $letter => $value ];
1071             }
1072             next unless @sf;
1073             $sf[-1][1] = $sf[-1][1] . '.' if $sf[-1][1] !~ /\.$/;
1074             $record->append( MARC::Moose::Field::Std->new(
1075             tag => $to, subf => \@sf ) );
1076             }
1077             }
1078              
1079             # 675 => 080, $v and $z aren't converted
1080             for my $field ( $unimarc->field('675') ) {
1081             my @sf = grep { $_->[0] !~ /v|z/; } @{$field->subf};
1082             $record->append( MARC::Moose::Field::Std->new(
1083             tag => '080', subf => \@sf ) );
1084             }
1085              
1086             # 676 => 082, $v => $2
1087             for my $field ( $unimarc->field('676') ) {
1088             my @sf = map { $_->[0] = '2' if $_->[0] eq 'v'; $_; } @{$field->subf};
1089             $record->append( MARC::Moose::Field::Std->new(
1090             tag => '082', subf => \@sf ) );
1091             }
1092              
1093             # Les auteurs 700 => 100,
1094             # Suppr sous $3, $6 et $7 $9
1095             for my $fromto ( ( [700, 100], [701, 700], [702, 700] ) ) {
1096             my ($from, $to) = @$fromto;
1097             for my $field ( $unimarc->field($from) ) {
1098             my $ind1 = $field->ind2;
1099             my @sf;
1100             my @codes;
1101             for ( @{$field->subf} ) {
1102             my ($letter, $value) = @$_;
1103             for ($letter) {
1104             if ( /a/ ) {
1105             push @sf, [ a => $value ];
1106             }
1107             elsif ( /b/ ) {
1108             if ( @sf ) {
1109             $sf[-1]->[1] .= ", $value";
1110             }
1111             else {
1112             push @sf, [ a => $value ];
1113             }
1114             }
1115             elsif ( /c/ ) {
1116             $sf[-1]->[1] .= ',';
1117             push @sf, [ c => $value ];
1118             }
1119             elsif ( /d/ ) {
1120             push @sf, [ b => $value ];
1121             }
1122             elsif ( /f/ ) {
1123             $sf[-1]->[1] .= ',' if @sf;
1124             push @sf, [ d => $value ];
1125             }
1126             elsif ( /g/ ) {
1127             $sf[-1]->[1] .= '(';
1128             push @sf, [ q => "$value)" ];
1129             }
1130             elsif ( /4/ ) {
1131             next if $from eq '700' && $value eq '070';
1132             my $code = $authcode{$value};
1133             next unless $code;
1134             push @codes, $code->[0];
1135             }
1136             }
1137             }
1138             next unless @sf;
1139             my $value = $sf[-1]->[1];
1140             $value =~ s/ *$//;
1141             $value =~ s/\.*$//;
1142             $value .= '.' if $value !~ /[-\?]$/;
1143             $sf[-1]->[1] = $value;
1144             push @sf, [ 4 => $_ ] for @codes;
1145             $record->append( MARC::Moose::Field::Std->new(
1146             tag => $to, ind1 => $field->ind2, subf => \@sf ) );
1147             }
1148             }
1149              
1150             # Les collectivités
1151             # Suppr sous $3, $6 et $7 $9
1152             SUBFIELD_CORPORATE:
1153             for my $fromto ( ( [710, 110, 111], [711, 710, 711], [712, 710, 711] ) ) {
1154             my ($from, $to_corporate, $to_meeting) = @$fromto;
1155             for my $field ( $unimarc->field($from) ) {
1156             my @sf;
1157             my @codes;
1158             for ( @{$field->subf} ) {
1159             my ($letter, $value) = @$_;
1160             for ($letter) {
1161             if ( /a/ ) {
1162             push @sf, [ a => $value ];
1163             }
1164             elsif ( /g/ ) {
1165             $value = "($value)" unless $value =~ /^\(/;
1166             $sf[-1]->[1] .= " $value" if @sf;
1167             }
1168             elsif ( /h/ ) {
1169             $sf[-1]->[1] .= " $value";
1170             }
1171             elsif ( /g/ ) {
1172             $sf[-1]->[1] .= " ($value)";
1173             }
1174             elsif ( /b/ ) {
1175             if ( @sf ) {
1176             $sf[-1]->[1] .= '.' unless $sf[-1]->[1] =~ /\.$/;
1177             }
1178             push @sf, [ b => $value ];
1179             }
1180             elsif ( /d/ ) {
1181             $value = "($value" unless $value =~ /^\(/;
1182             push @sf, [ n => $value ];
1183             }
1184             elsif ( /e/ ) {
1185             $value = " :$value)";
1186             push @sf, [ c => $value ];
1187             }
1188             elsif ( /f/ ) {
1189             $value = $sf[-1]->[0] eq 'n'
1190             ? " :$value"
1191             : "($value" if @sf;
1192             push @sf, [ d => $value ];
1193             }
1194             elsif ( /4/ ) {
1195             next SUBFIELD_CORPORATE if $from eq '700' && $value eq '070';
1196             my $code = $authcode{$value};
1197             next SUBFIELD_CORPORATE unless $code;
1198             push @codes, $code->[0];
1199             }
1200             }
1201             }
1202             next unless @sf;
1203             my $value = $sf[-1]->[1];
1204             $value =~ s/ *$//;
1205             $value =~ s/\.*$//;
1206             $value .= '.';
1207             $sf[-1]->[1] = $value;
1208             push @sf, [ 4 => $_ ] for @codes;
1209             my $to = $field->ind1 eq '1' ? $to_meeting : $to_corporate;
1210             $record->append( MARC::Moose::Field::Std->new(
1211             tag => $to, ind1 => $field->ind2, subf => \@sf ) );
1212             }
1213             }
1214              
1215             # Populate non-filing indicator based on UNIMARC NSB/NSE
1216             {
1217             my $first = 1;
1218             for my $tags (@$nonfiling_tags) {
1219             for my $tag (@$tags) {
1220             for my $field ($record->field($tag)) {
1221             for (@{$field->subf}) {
1222             next if $_->[0] ne 'a';
1223             # Found Main title
1224             my $title = $_->[1];
1225             next unless $title;
1226             for my $ns (@$ns_characters) {
1227             my ($nsb, $nse) = @$ns;
1228             next if $title !~ /^$nsb(.*)$nse(.)/;
1229             my $len = length($1);
1230             $len++ if $2 eq ' ';
1231             $len = 0 if $len >= 10;
1232             $title =~ s/$nsb//g;
1233             $title =~ s/$nse//g;
1234             $_->[1] = $title;
1235             if ($first) { $field->ind1($len); }
1236             else { $field->ind2($len); }
1237             last;
1238             }
1239             last;
1240             }
1241             }
1242             }
1243             $first = 0;
1244             }
1245             }
1246              
1247             # Some fields are kept, as they are: 856, 801, 9xx
1248             if ( my @fields = $unimarc->field('801|856|9..') ) {
1249             $record->append(@fields)
1250             }
1251              
1252             # Clean non-filing characters in all fields
1253             for my $field (@{$record->fields}) {
1254             next if ref $field eq 'MARC::Moose::Field::Control';
1255             for (@{$field->subf} ) {
1256             next if $_->[0] !~ /[a-z0-9]/;
1257             $_->[1] =~ s/\x08|\x09//g;
1258             }
1259             }
1260              
1261             return $record;
1262             };
1263              
1264             __PACKAGE__->meta->make_immutable;
1265              
1266             1;
1267              
1268             __END__
1269              
1270             =pod
1271              
1272             =encoding UTF-8
1273              
1274             =head1 NAME
1275              
1276             MARC::Moose::Formater::UnimarcToMarc21 - Convert biblio record from UNIMARC to MARC21
1277              
1278             =head1 VERSION
1279              
1280             version 1.0.49
1281              
1282             =head1 SYNOPSYS
1283              
1284             Read a UNIMARC ISO2709 file and dump it to STDOUT in text transformed into
1285             MARC21:
1286              
1287             my $reader = MARC::Moose::Reader::File::Iso2709->new(
1288             file => 'biblio-unimarc.iso' );
1289             my $formater = MARC::Moose::Formater::UnimarcToMarc21->new();
1290             while ( my $unimarc = $reader->read() ) {
1291             my $marc21 = $formater->format($unimarc);
1292             print $marc21->as('Text');
1293             }
1294              
1295             Same with shortcut:
1296              
1297             my $reader = MARC::Moose::Reader::File::Iso2709->new(
1298             file => 'biblio-unimarc.iso' );
1299             while ( my $unimarc = $reader->read() ) {
1300             print $unimarc->as('UnimarcToMarc21')->as('Text');
1301             }
1302              
1303             Read a UNIMARC ISO2709 file and dump it to another ISO2709 file transformed
1304             into MARC21:
1305              
1306             my $reader = MARC::Moose::Reader::File::Iso2709->new(
1307             file => 'biblio-unimarc.iso' );
1308             my $writer = MARC::Moose::Writer->new(
1309             fh => IO::File->new('koha.mrc', '>:encoding(utf8)'),
1310             formater => MARC::Moose::Formater::Iso2709->new() )
1311             );
1312             my $tomarc21 = MARC::Moose::Formater::UnimarcToMarc21->new();
1313             while ( my $unimarc = $reader->read() ) {
1314             $writer->write( $tomarc21->format($unimarc) );
1315             }
1316              
1317             =head1 COMMAND LINE
1318              
1319             If you don't want to write a Perl script, you can use the L<marcmoose> command.
1320             This way, you can for example convert a ISO 2709 UNIMARC file named
1321             C<unimarc.iso> into a ISO 2709 MARC21 file named C<marc.iso>:
1322              
1323             marcmoose --parser iso2709 --formater iso2709 --converter unimarctomarc21
1324             --output marc.iso unimarc.iso
1325              
1326             =head1 AUTHOR
1327              
1328             Frédéric Demians <f.demians@tamil.fr>
1329              
1330             =head1 COPYRIGHT AND LICENSE
1331              
1332             This software is copyright (c) 2024 by Frédéric Demians.
1333              
1334             This is free software; you can redistribute it and/or modify it under
1335             the same terms as the Perl 5 programming language system itself.
1336              
1337             =cut